forgegraph 0.3.0__tar.gz → 0.4.0__tar.gz

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 (377) hide show
  1. {forgegraph-0.3.0 → forgegraph-0.4.0}/.pre-commit-config.yaml +1 -0
  2. {forgegraph-0.3.0 → forgegraph-0.4.0}/CHANGELOG.md +30 -0
  3. {forgegraph-0.3.0 → forgegraph-0.4.0}/CONTRIBUTING.md +4 -5
  4. {forgegraph-0.3.0 → forgegraph-0.4.0}/PKG-INFO +16 -19
  5. {forgegraph-0.3.0 → forgegraph-0.4.0}/README.md +15 -18
  6. {forgegraph-0.3.0 → forgegraph-0.4.0}/docs/design/architecture.md +51 -52
  7. forgegraph-0.4.0/docs/packs/acme/formats/slide.md +14 -0
  8. {forgegraph-0.3.0/docs/specs/pack → forgegraph-0.4.0/docs/packs}/acme/graphspecs/polarization-curve.md +14 -19
  9. forgegraph-0.4.0/docs/packs/acme/layouts/dashboard.md +17 -0
  10. {forgegraph-0.3.0/docs/specs/pack → forgegraph-0.4.0/docs/packs}/acme/pack.md +1 -1
  11. forgegraph-0.4.0/docs/packs/acme/schedules/workday.md +16 -0
  12. forgegraph-0.4.0/docs/packs/acme/themes/acme.md +26 -0
  13. forgegraph-0.4.0/docs/packs/forgegraph_default/catalogs/formats/compact_format.md +11 -0
  14. forgegraph-0.4.0/docs/packs/forgegraph_default/catalogs/formats/wide_format.md +11 -0
  15. forgegraph-0.4.0/docs/packs/forgegraph_default/catalogs/themes/light_theme.md +58 -0
  16. forgegraph-0.4.0/docs/packs/forgegraph_default/formats/compact_format.md +35 -0
  17. forgegraph-0.4.0/docs/packs/forgegraph_default/formats/wide_format.md +48 -0
  18. forgegraph-0.4.0/docs/packs/forgegraph_default/graphspecs/graphspec_example.md +116 -0
  19. forgegraph-0.4.0/docs/packs/forgegraph_default/layouts/layout.md +31 -0
  20. forgegraph-0.4.0/docs/packs/forgegraph_default/pack.md +29 -0
  21. forgegraph-0.4.0/docs/packs/forgegraph_default/schedules/schedule.md +30 -0
  22. forgegraph-0.4.0/docs/packs/forgegraph_default/themes/light_theme.md +153 -0
  23. forgegraph-0.4.0/docs/specifications/catalogs/graphtype_xy.md +55 -0
  24. forgegraph-0.4.0/docs/specifications/catalogs/layer_error_bar.md +18 -0
  25. forgegraph-0.4.0/docs/specifications/catalogs/layer_note.md +15 -0
  26. forgegraph-0.4.0/docs/specifications/catalogs/layer_reference.md +35 -0
  27. forgegraph-0.4.0/docs/specifications/config.md +24 -0
  28. forgegraph-0.4.0/docs/specifications/format.md +19 -0
  29. forgegraph-0.4.0/docs/specifications/graphspec_example.md +129 -0
  30. forgegraph-0.4.0/docs/specifications/graphtype_xy.md +74 -0
  31. forgegraph-0.4.0/docs/specifications/layer_error_bar.md +15 -0
  32. forgegraph-0.4.0/docs/specifications/layer_note.md +12 -0
  33. forgegraph-0.4.0/docs/specifications/layer_reference.md +55 -0
  34. forgegraph-0.4.0/docs/specifications/layout_example.md +28 -0
  35. forgegraph-0.4.0/docs/specifications/pack.md +25 -0
  36. {forgegraph-0.3.0/docs/specs/provenance → forgegraph-0.4.0/docs/specifications}/provenance.md +9 -9
  37. forgegraph-0.4.0/docs/specifications/schedule.md +30 -0
  38. forgegraph-0.4.0/docs/specifications/styles.md +12 -0
  39. forgegraph-0.4.0/docs/specifications/theme.md +109 -0
  40. forgegraph-0.4.0/docs/specs_and_packs_revisions/notes.docx +0 -0
  41. forgegraph-0.4.0/examples/layouts/dashboard/forgegraph.toml +1 -0
  42. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/lab_throughput.layout.md +4 -4
  43. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/lab_throughput.layout.yaml +4 -4
  44. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/lost_runs.yaml +13 -17
  45. forgegraph-0.4.0/examples/layouts/dashboard/ops/formats/dashboard.md +17 -0
  46. forgegraph-0.4.0/examples/layouts/dashboard/ops/formats/dashboard.yaml +13 -0
  47. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/run_time.yaml +13 -17
  48. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/runs_gc_ms_01.yaml +5 -8
  49. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/runs_hplc_01.yaml +5 -8
  50. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/runs_hplc_02.yaml +5 -8
  51. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/runs_icp_oes_01.yaml +5 -8
  52. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/samples_weekly.yaml +5 -8
  53. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/throughput_weekly.yaml +6 -11
  54. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/paper/cell_performance.layout.md +3 -3
  55. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/paper/cell_performance.layout.yaml +3 -3
  56. forgegraph-0.4.0/examples/layouts/paper/forgegraph.toml +1 -0
  57. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/paper/hfr_time.yaml +8 -12
  58. forgegraph-0.4.0/examples/layouts/paper/journal/formats/journal-double.md +16 -0
  59. forgegraph-0.4.0/examples/layouts/paper/journal/formats/journal-double.yaml +12 -0
  60. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/paper/polarization.yaml +6 -9
  61. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/paper/voltage_time.yaml +8 -12
  62. forgegraph-0.4.0/examples/packs/acme/formats/slide.yaml +10 -0
  63. forgegraph-0.4.0/examples/packs/acme/graphspecs/polarization-curve.yaml +27 -0
  64. forgegraph-0.4.0/examples/packs/acme/layouts/dashboard.yaml +13 -0
  65. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/packs/acme/pack.yaml +1 -1
  66. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/packs/acme/references/house.md +1 -1
  67. forgegraph-0.4.0/examples/packs/acme/schedules/workday.yaml +12 -0
  68. forgegraph-0.4.0/examples/packs/acme/themes/acme.yaml +22 -0
  69. {forgegraph-0.3.0 → forgegraph-0.4.0}/pyproject.toml +1 -1
  70. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/mirror_specs.py +3 -2
  71. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/run_skill_evals.py +51 -16
  72. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/smoke_installed.py +8 -12
  73. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/visual_changelog.py +5 -4
  74. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/catalog_build/build.py +15 -11
  75. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/catalog_build/cards.py +43 -19
  76. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/catalog_build/examples.py +51 -11
  77. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/catalog_build/files.py +48 -23
  78. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/cli/catalog.py +4 -4
  79. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/cli/graphs.py +3 -1
  80. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/cli/schema.py +28 -14
  81. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/data.py +2 -2
  82. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/axes.py +67 -10
  83. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/colorbar.py +45 -14
  84. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/dates.py +8 -3
  85. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/figure.py +2 -2
  86. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/key.py +34 -27
  87. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/labels.py +39 -25
  88. forgegraph-0.4.0/src/forgegraph/drawing/layers.py +874 -0
  89. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/layout.py +21 -20
  90. forgegraph-0.4.0/src/forgegraph/drawing/limits.py +50 -0
  91. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/lint.py +35 -20
  92. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/marks.py +224 -149
  93. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/spacing.py +28 -13
  94. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/style.py +91 -26
  95. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/ticks.py +7 -9
  96. forgegraph-0.4.0/src/forgegraph/drawing/xy.py +1196 -0
  97. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/forgemark.py +1 -1
  98. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/graph.py +74 -27
  99. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/jobs/__init__.py +1 -1
  100. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/jobs/run.py +3 -3
  101. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/jobs/status.py +1 -1
  102. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/layouts/__init__.py +31 -20
  103. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/layouts/separators.py +71 -26
  104. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/packs/__init__.py +6 -6
  105. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/catalogs/formats/compact_format.yaml +7 -0
  106. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/catalogs/formats/wide_format.yaml +7 -0
  107. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/catalogs/themes/light_theme.yaml +54 -0
  108. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/formats/compact_format.yaml +15 -0
  109. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/formats/wide_format.yaml +15 -0
  110. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/graphspecs/graphspec_example.yaml +112 -0
  111. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/layouts/layout.yaml +27 -0
  112. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/pack.yaml +5 -0
  113. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/schedules/schedule.yaml +26 -0
  114. forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default/themes/light_theme.yaml +149 -0
  115. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/skill/forgegraph/SKILL.md +2 -2
  116. forgegraph-0.4.0/src/forgegraph/skill/forgegraph/examples/bars.yaml +32 -0
  117. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/skill/forgegraph/examples/figure.layout.yaml +4 -4
  118. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/skill/forgegraph/examples/figure.schedule.yaml +3 -3
  119. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/skill/forgegraph/examples/layers.yaml +13 -15
  120. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/skill/forgegraph/examples/xy.yaml +7 -12
  121. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/skill/forgegraph/references/themes_formats_configs.md +3 -3
  122. forgegraph-0.4.0/src/forgegraph/spec/__init__.py +12 -0
  123. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/spec/access.py +4 -4
  124. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/spec/explain.py +16 -3
  125. forgegraph-0.4.0/src/forgegraph/spec/layers.py +229 -0
  126. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/spec/layout.py +31 -16
  127. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/spec/loaded.py +2 -2
  128. forgegraph-0.4.0/src/forgegraph/spec/parameters.py +858 -0
  129. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/spec/provenance.py +19 -20
  130. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/spec/registry.py +23 -18
  131. forgegraph-0.4.0/src/forgegraph/spec/resolve.py +231 -0
  132. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/spec/schedule.py +5 -3
  133. forgegraph-0.4.0/src/forgegraph/spec/series.py +169 -0
  134. forgegraph-0.4.0/src/forgegraph/spec/tags.py +230 -0
  135. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/spec/validate.py +109 -60
  136. forgegraph-0.4.0/src/forgegraph/specifications/catalogs/graphtype_xy.yaml +51 -0
  137. forgegraph-0.4.0/src/forgegraph/specifications/catalogs/layer_error_bar.yaml +14 -0
  138. forgegraph-0.4.0/src/forgegraph/specifications/catalogs/layer_note.yaml +11 -0
  139. forgegraph-0.4.0/src/forgegraph/specifications/catalogs/layer_reference.yaml +31 -0
  140. forgegraph-0.4.0/src/forgegraph/specifications/config.toml +1 -0
  141. forgegraph-0.4.0/src/forgegraph/specifications/format.yaml +15 -0
  142. forgegraph-0.4.0/src/forgegraph/specifications/graphspec_example.yaml +125 -0
  143. forgegraph-0.4.0/src/forgegraph/specifications/graphtype_xy.yaml +70 -0
  144. forgegraph-0.4.0/src/forgegraph/specifications/layer_error_bar.yaml +11 -0
  145. forgegraph-0.4.0/src/forgegraph/specifications/layer_note.yaml +8 -0
  146. forgegraph-0.4.0/src/forgegraph/specifications/layer_reference.yaml +22 -0
  147. forgegraph-0.4.0/src/forgegraph/specifications/layout_example.yaml +24 -0
  148. forgegraph-0.4.0/src/forgegraph/specifications/pack.yaml +5 -0
  149. {forgegraph-0.3.0/src/forgegraph/spec → forgegraph-0.4.0/src/forgegraph/specifications}/provenance.yaml +8 -8
  150. forgegraph-0.4.0/src/forgegraph/specifications/schedule.yaml +26 -0
  151. forgegraph-0.4.0/src/forgegraph/specifications/styles.yaml +8 -0
  152. forgegraph-0.4.0/src/forgegraph/specifications/theme.yaml +105 -0
  153. forgegraph-0.4.0/tests/baselines/flow_sweep_wide.png +0 -0
  154. forgegraph-0.4.0/tests/baselines/layout_anode_layers.png +0 -0
  155. forgegraph-0.4.0/tests/baselines/layout_screening_and_rate.png +0 -0
  156. forgegraph-0.4.0/tests/baselines/separator_colorbar.png +0 -0
  157. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/pack_helpers.py +26 -19
  158. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/recreations.py +33 -27
  159. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/render_helpers.py +26 -7
  160. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/spec_mirror.py +63 -78
  161. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_axes.py +101 -34
  162. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_blank.py +9 -7
  163. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_catalog.py +4 -14
  164. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_catalog_files.py +48 -34
  165. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_catalog_inputs.py +19 -10
  166. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_catalog_page.py +12 -9
  167. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_cli.py +39 -32
  168. forgegraph-0.4.0/tests/test_dates.py +478 -0
  169. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_jobs.py +10 -10
  170. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_key_position.py +13 -7
  171. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_layers.py +315 -118
  172. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_layout.py +134 -48
  173. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_lint.py +99 -3
  174. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_marks.py +98 -71
  175. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_packs.py +60 -54
  176. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_render.py +243 -78
  177. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_schedule.py +1 -1
  178. forgegraph-0.4.0/tests/test_series.py +294 -0
  179. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_spec_comments.py +19 -20
  180. forgegraph-0.4.0/tests/test_specs.py +938 -0
  181. forgegraph-0.4.0/tests/test_styles.py +234 -0
  182. forgegraph-0.4.0/tests/test_tags.py +140 -0
  183. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_text.py +3 -3
  184. {forgegraph-0.3.0 → forgegraph-0.4.0}/uv.lock +1 -1
  185. forgegraph-0.3.0/docs/specs/catalog/base_format.md +0 -11
  186. forgegraph-0.3.0/docs/specs/catalog/base_layout_format.md +0 -11
  187. forgegraph-0.3.0/docs/specs/catalog/base_theme.md +0 -81
  188. forgegraph-0.3.0/docs/specs/catalog/graph_type_xy.md +0 -97
  189. forgegraph-0.3.0/docs/specs/catalog/layer_error_bar.md +0 -17
  190. forgegraph-0.3.0/docs/specs/catalog/layer_note.md +0 -15
  191. forgegraph-0.3.0/docs/specs/catalog/layer_reference_line.md +0 -22
  192. forgegraph-0.3.0/docs/specs/config/config.md +0 -5
  193. forgegraph-0.3.0/docs/specs/format/base_format.md +0 -22
  194. forgegraph-0.3.0/docs/specs/format/base_layout_format.md +0 -14
  195. forgegraph-0.3.0/docs/specs/graph_type/xy.md +0 -97
  196. forgegraph-0.3.0/docs/specs/graphspec/polarization.md +0 -155
  197. forgegraph-0.3.0/docs/specs/layer/error_bar.md +0 -16
  198. forgegraph-0.3.0/docs/specs/layer/note.md +0 -12
  199. forgegraph-0.3.0/docs/specs/layer/reference_line.md +0 -19
  200. forgegraph-0.3.0/docs/specs/layout/base_layout.md +0 -31
  201. forgegraph-0.3.0/docs/specs/pack/acme/formats/slide.md +0 -16
  202. forgegraph-0.3.0/docs/specs/pack/acme/layouts/dashboard.md +0 -18
  203. forgegraph-0.3.0/docs/specs/pack/acme/schedules/workday.md +0 -18
  204. forgegraph-0.3.0/docs/specs/pack/acme/themes/acme.md +0 -28
  205. forgegraph-0.3.0/docs/specs/pack/forgegraph_base.md +0 -9
  206. forgegraph-0.3.0/docs/specs/pack/forgegraph_base_2/catalogs/formats/compact_format.md +0 -11
  207. forgegraph-0.3.0/docs/specs/pack/forgegraph_base_2/catalogs/themes/base_theme.md +0 -25
  208. forgegraph-0.3.0/docs/specs/pack/forgegraph_base_2/formats/compact_format.md +0 -17
  209. forgegraph-0.3.0/docs/specs/pack/forgegraph_base_2/graphspecs/example.md +0 -17
  210. forgegraph-0.3.0/docs/specs/pack/forgegraph_base_2/pack.md +0 -9
  211. forgegraph-0.3.0/docs/specs/pack/forgegraph_base_2/themes/base_theme.md +0 -79
  212. forgegraph-0.3.0/docs/specs/schedule/base_schedule.md +0 -32
  213. forgegraph-0.3.0/docs/specs/theme/base_theme.md +0 -176
  214. forgegraph-0.3.0/examples/layouts/dashboard/forgegraph.toml +0 -1
  215. forgegraph-0.3.0/examples/layouts/dashboard/ops/formats/dashboard.md +0 -20
  216. forgegraph-0.3.0/examples/layouts/dashboard/ops/formats/dashboard.yaml +0 -16
  217. forgegraph-0.3.0/examples/layouts/paper/forgegraph.toml +0 -1
  218. forgegraph-0.3.0/examples/layouts/paper/journal/formats/journal-double.md +0 -18
  219. forgegraph-0.3.0/examples/layouts/paper/journal/formats/journal-double.yaml +0 -14
  220. forgegraph-0.3.0/examples/packs/acme/formats/slide.yaml +0 -12
  221. forgegraph-0.3.0/examples/packs/acme/graphspecs/polarization-curve.yaml +0 -32
  222. forgegraph-0.3.0/examples/packs/acme/layouts/dashboard.yaml +0 -14
  223. forgegraph-0.3.0/examples/packs/acme/schedules/workday.yaml +0 -14
  224. forgegraph-0.3.0/examples/packs/acme/themes/acme.yaml +0 -24
  225. forgegraph-0.3.0/src/forgegraph/catalogs/graph_types/xy.yaml +0 -93
  226. forgegraph-0.3.0/src/forgegraph/catalogs/layers/error_bar.yaml +0 -13
  227. forgegraph-0.3.0/src/forgegraph/catalogs/layers/note.yaml +0 -11
  228. forgegraph-0.3.0/src/forgegraph/catalogs/layers/reference_line.yaml +0 -18
  229. forgegraph-0.3.0/src/forgegraph/drawing/layers.py +0 -590
  230. forgegraph-0.3.0/src/forgegraph/drawing/xy.py +0 -882
  231. forgegraph-0.3.0/src/forgegraph/graph_types/xy.yaml +0 -93
  232. forgegraph-0.3.0/src/forgegraph/layers/error_bar.yaml +0 -12
  233. forgegraph-0.3.0/src/forgegraph/layers/note.yaml +0 -8
  234. forgegraph-0.3.0/src/forgegraph/layers/reference_line.yaml +0 -15
  235. forgegraph-0.3.0/src/forgegraph/packs/config.toml +0 -1
  236. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/catalogs/formats/base_format.yaml +0 -7
  237. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/catalogs/formats/base_layout_format.yaml +0 -7
  238. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/catalogs/themes/base_theme.yaml +0 -77
  239. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/formats/base_format.yaml +0 -18
  240. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/formats/base_layout_format.yaml +0 -10
  241. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/graphspecs/polarization.yaml +0 -151
  242. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/layouts/base_layout.yaml +0 -27
  243. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/pack.yaml +0 -5
  244. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/schedules/base_schedule.yaml +0 -28
  245. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base/themes/base_theme.yaml +0 -172
  246. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/catalogs/formats/compact_format.yaml +0 -7
  247. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/catalogs/themes/base_theme.yaml +0 -21
  248. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/formats/compact_format.yaml +0 -13
  249. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/graphspecs/dated.parquet +0 -0
  250. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/graphspecs/example.yaml +0 -13
  251. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/graphspecs/fanned.parquet +0 -0
  252. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/graphspecs/polarization.parquet +0 -0
  253. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/graphspecs/reinforced.parquet +0 -0
  254. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/pack.yaml +0 -5
  255. forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base_2/themes/base_theme.yaml +0 -75
  256. forgegraph-0.3.0/src/forgegraph/skill/forgegraph/examples/bars.yaml +0 -35
  257. forgegraph-0.3.0/src/forgegraph/spec/__init__.py +0 -13
  258. forgegraph-0.3.0/src/forgegraph/spec/layers.py +0 -126
  259. forgegraph-0.3.0/src/forgegraph/spec/parameters.py +0 -480
  260. forgegraph-0.3.0/src/forgegraph/spec/resolve.py +0 -162
  261. forgegraph-0.3.0/tests/baselines/flow_sweep_wide.png +0 -0
  262. forgegraph-0.3.0/tests/baselines/layout_anode_layers.png +0 -0
  263. forgegraph-0.3.0/tests/baselines/layout_screening_and_rate.png +0 -0
  264. forgegraph-0.3.0/tests/baselines/separator_colorbar.png +0 -0
  265. forgegraph-0.3.0/tests/test_specs.py +0 -664
  266. {forgegraph-0.3.0 → forgegraph-0.4.0}/.gitignore +0 -0
  267. {forgegraph-0.3.0 → forgegraph-0.4.0}/LICENSE +0 -0
  268. {forgegraph-0.3.0 → forgegraph-0.4.0}/TRADEMARKS.md +0 -0
  269. {forgegraph-0.3.0 → forgegraph-0.4.0}/docs/design/initial-specification.md +0 -0
  270. {forgegraph-0.3.0 → forgegraph-0.4.0}/docs/design/todo.md +0 -0
  271. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/README.md +0 -0
  272. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/lost_runs.parquet +0 -0
  273. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/ops/pack.yaml +0 -0
  274. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/prep_lab_dashboard.py +0 -0
  275. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/run_time.parquet +0 -0
  276. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/weekly_gc_ms_01.parquet +0 -0
  277. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/weekly_hplc_01.parquet +0 -0
  278. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/weekly_hplc_02.parquet +0 -0
  279. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/weekly_icp_oes_01.parquet +0 -0
  280. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/weekly_runs.parquet +0 -0
  281. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/dashboard/weekly_samples.parquet +0 -0
  282. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/paper/cell_durability.parquet +0 -0
  283. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/paper/cell_polarization.parquet +0 -0
  284. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/paper/journal/pack.yaml +0 -0
  285. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/layouts/paper/prep_cells.py +0 -0
  286. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/README.md +0 -0
  287. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/air_passengers.csv +0 -0
  288. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/air_passengers_by_month.csv +0 -0
  289. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/anscombe.csv +0 -0
  290. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/assay_summary.csv +0 -0
  291. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/cell_durability.csv +0 -0
  292. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/cell_polarization.csv +0 -0
  293. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/co2_monthly.csv +0 -0
  294. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/diamonds.csv +0 -0
  295. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/dose_response.csv +0 -0
  296. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/economics.csv +0 -0
  297. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/enzyme_kinetics.csv +0 -0
  298. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/fmri.csv +0 -0
  299. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/gapminder.csv +0 -0
  300. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/geyser.csv +0 -0
  301. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/growth.csv +0 -0
  302. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/iris.csv +0 -0
  303. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/lab_export.xlsx +0 -0
  304. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/lab_runs.xlsx +0 -0
  305. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/penguins.csv +0 -0
  306. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/seaice.csv +0 -0
  307. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/sensor_log_eu.csv +0 -0
  308. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/stocks.csv +0 -0
  309. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/tips.csv +0 -0
  310. {forgegraph-0.3.0 → forgegraph-0.4.0}/examples/sample-data/weather.xlsx +0 -0
  311. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/build_example_data.py +0 -0
  312. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/build_recreations.py +0 -0
  313. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/build_sample_data.py +0 -0
  314. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/check_denylist.py +0 -0
  315. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/forgemark.py +0 -0
  316. {forgegraph-0.3.0 → forgegraph-0.4.0}/scripts/report_skill_evals.py +0 -0
  317. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/__init__.py +0 -0
  318. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/__main__.py +0 -0
  319. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/canvas.py +0 -0
  320. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/catalog_build/__init__.py +0 -0
  321. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/catalog_build/page.py +0 -0
  322. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/cli/__init__.py +0 -0
  323. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/cli/jobs.py +0 -0
  324. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/cli/skill.py +0 -0
  325. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/__init__.py +0 -0
  326. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/errors.py +0 -0
  327. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/export.py +0 -0
  328. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/markup.py +0 -0
  329. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/page.py +0 -0
  330. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawing/text.py +0 -0
  331. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/drawn.py +0 -0
  332. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/IBMPlexSerif-Bold.ttf +0 -0
  333. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/IBMPlexSerif-Light.ttf +0 -0
  334. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/IBMPlexSerif-Medium.ttf +0 -0
  335. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/IBMPlexSerif-Regular.ttf +0 -0
  336. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/IBMPlexSerif-SemiBold.ttf +0 -0
  337. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/OFL-IBMPlexSerif.txt +0 -0
  338. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/OFL-Roboto.txt +0 -0
  339. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/OFL-SourceSans3.md +0 -0
  340. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/Roboto-Bold.ttf +0 -0
  341. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/Roboto-Italic.ttf +0 -0
  342. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/Roboto-Light.ttf +0 -0
  343. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/Roboto-Medium.ttf +0 -0
  344. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/Roboto-Regular.ttf +0 -0
  345. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/Roboto-SemiBold.ttf +0 -0
  346. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/SourceSans3-Bold.ttf +0 -0
  347. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/SourceSans3-It.ttf +0 -0
  348. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/SourceSans3-Light.ttf +0 -0
  349. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/SourceSans3-Medium.ttf +0 -0
  350. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/SourceSans3-Regular.ttf +0 -0
  351. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/SourceSans3-Semibold.ttf +0 -0
  352. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/fonts/__init__.py +0 -0
  353. {forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base → forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default}/graphspecs/dated.parquet +0 -0
  354. {forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base → forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default}/graphspecs/fanned.parquet +0 -0
  355. {forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base → forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default}/graphspecs/polarization.parquet +0 -0
  356. {forgegraph-0.3.0/src/forgegraph/packs/forgegraph_base → forgegraph-0.4.0/src/forgegraph/packs/forgegraph_default}/graphspecs/reinforced.parquet +0 -0
  357. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/skill/__init__.py +0 -0
  358. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/skill/forgegraph/examples/xy.parquet +0 -0
  359. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/skill/forgegraph/references/requests.md +0 -0
  360. {forgegraph-0.3.0 → forgegraph-0.4.0}/src/forgegraph/spec/common.py +0 -0
  361. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/baselines/crowded_runs.png +0 -0
  362. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/baselines/fuel_by_size.png +0 -0
  363. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/baselines/polarization.png +0 -0
  364. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/baselines/polarization_narrow.png +0 -0
  365. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/baselines/precipitation_loglog.png +0 -0
  366. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/baselines/replicates.png +0 -0
  367. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/baselines/screening_scatter.png +0 -0
  368. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/conftest.py +0 -0
  369. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/fixtures/catalog_comments.html +0 -0
  370. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_denylist.py +0 -0
  371. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_labels.py +0 -0
  372. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_markup.py +0 -0
  373. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_packaging.py +0 -0
  374. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_perturbation.py +0 -0
  375. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_regression.py +0 -0
  376. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_smoke.py +0 -0
  377. {forgegraph-0.3.0 → forgegraph-0.4.0}/tests/test_spec_mirrors.py +0 -0
@@ -3,6 +3,7 @@ repos:
3
3
  rev: v6.0.0
4
4
  hooks:
5
5
  - id: check-yaml
6
+ args: [--unsafe] # syntax only, so specs may carry !var and !fix tags
6
7
  - id: check-toml
7
8
  - id: end-of-file-fixer
8
9
  exclude: \.md$|^(review|catalog|docs/visual-changelog)/
@@ -2,6 +2,36 @@
2
2
 
3
3
  ForgeGraph follows [semantic versioning](https://semver.org). Before 1.0, a minor version may change how graphs look or how specs are written. From 0.2.0 on, each release's entry links its visual changelog: before and after images of every catalog card and regression graph that changed, made with `scripts/visual_changelog.py`.
4
4
 
5
+ ## 0.4.0 (2026-10-27)
6
+
7
+ Images that changed: the [visual changelog](docs/visual-changelog/0.4.0.html).
8
+
9
+ ### Changed
10
+
11
+ - **Breaking for existing specs and configs:** one built-in pack, `forgegraph_default`, replaces `forgegraph_base` and `forgegraph_base_2`. Its specs are `light_theme` (in place of `forgegraph_base/base_theme`), `compact_format` (`base_format`), `wide_format` (`base_layout_format`), `layout` (`layout_example`), `schedule` (`base_schedule`) and `graphspec_example`; `forgegraph_base_2` has no replacement. A main config listing `forgegraph_base` or `forgegraph_base_2` is an error: list `forgegraph_default` instead. light_theme draws 0.3's look. It fixes every text's font and gap, the palettes (the colors, line styles, marker shapes and fills that series take in turn), the ticks' direction, sides and algorithms, and the grid, so a GraphSpec that sets `grid.x.show` needs a theme that leaves it variable; a series may still set its own color, line style, marker shape and line width. The default layout draws no line between tiles and leaves 0.25 × the base size at the figure's edges.
12
+ - The specification files, which define each kind, are in `src/forgegraph/specifications/`, and their pages in `docs/specifications/`; no spec can name or extend them. The built-in pack's pages are in `docs/packs/forgegraph_default/`, and the catalog page is `catalog/forgegraph_default.html`.
13
+ - A category's brackets in a specification file say what the code reads about it, from a fixed set: its style, `same as <sibling>` for a category with a sibling's parameters, and `look from <theme category>` for a layer category whose look comes from the theme. Anything else there is an error, and descriptions are never read, so rewording one changes nothing.
14
+ - A style's member that no file tags, itself or through a category inside `parameters`, is fixed or variable as its general parameter is: a fixed `text.font` fixes every text style's font, even where the theme sets the style's own.
15
+ - `forgegraph schema <kind>` no longer says whether a parameter is fixed: it shows the kind's specification, which isn't what graphs are drawn with. `forgegraph schema graphspec` lists what a GraphSpec can set, and `forgegraph explain` shows what's fixed.
16
+ - A reference layer's `fill.color` may be None: two lines with values make a band only when it isn't.
17
+ - **Breaking for existing specs:** a GraphSpec lists its series by name under `series`, each drawn from its own column (by default, the column of its name) with its own settings: `aggregation`, `split`, `order`, `color`, `color_scale`, `line`, `marker`, `bar` and `value_label`, and the theme's variable series parameters it sets for itself. They replace `axis.*.data.aggregation`, `encoding.*` and `mark.*`; the value axis's `data.column` is None. `arrangement.base_axis`, `arrangement.stacking`, `arrangement.offset` and `arrangement.total.show` replace `series.base_axis`, `series.aggregation`, `series.offset` and `series.value_label.total.show`. A series' color, line style, marker shape and fill take `first`, `series`, a value or a column; series take palette colors in the order they're listed.
18
+ - **Breaking for existing specs:** the `reference` layer replaces `reference_line`: `line_1` and `line_2`, each at a value or from a column, with its own `aggregation` and `offset` (aggregation styles), `scope`, line and label (with `label.inside`), and the `fill` of a band between them. The theme's `layer.reference` replaces `layer.reference_line`.
19
+ - An aggregation style (`function`, `multiplier`, `percentile`) for a series, a reference line and error bars; functions include `percentile`, `stddev`, `stderr` and `ci`.
20
+ - `line.interpolate` draws a series' line straight, in steps (`step_after`, `step_before`, `step_center`) or as a smooth curve that never overshoots its points.
21
+ - A note and a reference layer's lines, labels and fill may set the theme's variable look for themselves.
22
+ - A style key (one from `encoding.line.style.column`, `encoding.marker.shape.column` or `encoding.marker.fill.column`) follows `legend.key.title.text`, as the series' key does: `column_name`, the default, gives each key its own column's name, so default graphs are unchanged; `None` removes every key's title; a text titles one key, and with more than one key drawn it's an error. With no `encoding.color.column`, a style key's samples are drawn in the one series color, not the text color. `render --json` reports `style_keys`, the column each style key drawn explains (for a layout, each tile's), beside `labelling`.
23
+ - A range type `date`: a date, or a date and time, as YAML's bare date (`2025-08-25`, `2025-08-25 06:00:00`) or ISO 8601 text (`"2025-08-25T06:00"`); a time with a zone is at its clock time, as the data's are. `axis.x.min`, `axis.x.max`, `axis.y.min` and `axis.y.max`, a reference line's `value` and `benchmark.value` take it: on a datetime axis or column, a date is drawn exactly where its day number is, and numbers keep working as day numbers. A date on an axis that isn't dates is an error naming the axis's type. Text that reads as a date is a reference line's column only if the data has a column of that name. Provenance records write dates as ISO 8601 text.
24
+ - On a date value axis, what would draw nonsense is an error that says what to do: stacking (`series.aggregation` sum or percent), adding up rows (`axis.*.data.aggregation` sum), bars (`mark.bar.show`, which would start at 1970), and value labels without a date format. A reference line at a date column's aggregate is on a date axis only, at its mean, median, min or max, with multiplier 1. A date axis's `tick.label.format` must have a date directive.
25
+
26
+ ### Fixed
27
+
28
+ - In a theme whose text is light, powers of ten on a log axis and superscripts in labels no longer give a `font-fallback` lint issue. They were always drawn in the theme's font and weight; the issue came from the faces matplotlib loads for maths but doesn't draw these with.
29
+ - The `outside-limits` and `log-axis-values` lint checks count only data points. A reference line's ends and an error bar's ends no longer count as points outside a set axis limit, or as values of 0 or below on a log axis.
30
+ - A style key on the benchmark's column matches the drawing: its entries are the groups the series are styled by, so a value after the benchmark no longer gets the next value's style, and the benchmark, drawn in its own style, has no style sample and is named once, by `legend.method`. When `legend.method: key` doesn't fit, the error says why: every position in `legend.key.positions` is taken or covers data, naming the position each style key took.
31
+ - YAML's null (`null`, `~` or nothing) is None everywhere a spec can say None. `extends: null` failed with `KeyError: None`, and a file extending null wasn't required to list every parameter.
32
+ - Computed values are compared with an axis's limits allowing for float noise: a value is past a limit only if it's past by more than 1e-9 of the axis's span, measured in powers of ten on a log axis. A stack that totals `axis.y.max` but for its last digits isn't reported by `outside-limits`; a reference line at a computed value that equals a set limit isn't an error; bars whose values are 0 but for float noise still stand on the axis; and a tick a hair past the end of the view, which matplotlib draws, is measured for layout and lint and counts when log labels choose between plain numbers and powers of ten.
33
+ - Dates and times draw the same on y as on x: every value placed on an axis (the data, its aggregates, stacks, reference lines, error bars and limits) becomes a number the same way, so a date y axis has date ticks, a reference line at a date column's aggregate lands on it, and error bars from a date column's spread are in days. With value labels off, a date `tick.label.format` such as `"%H:%M"` on y is no longer refused as a number format; value labels and a reference line's value on a date axis are written in a date format.
34
+
5
35
  ## 0.3.0 (2026-10-25)
6
36
 
7
37
  Images that changed: the [visual changelog](docs/visual-changelog/0.3.0.html).
@@ -19,13 +19,12 @@ Mark a new test `@pytest.mark.slow` if it takes over a second (`uv run pytest --
19
19
 
20
20
  ## After changing a definition or how graphs are drawn
21
21
 
22
- Every parameter gets one catalog card, its value and baseline chosen by a rule (`src/forgegraph/catalog_build/cards.py`), so a new parameter needs no card of its own; the page shows the variable parameters' cards, and the perturbation test draws them all. It needs a baseline that shows it only if none does: the build (for a variable parameter) or the perturbation test (for any) names a parameter that changes no baseline's drawing, and the fix is a generic scenario added to the baselines in the matching catalog file under `src/forgegraph/catalogs/` (or a pack's `catalogs/`).
22
+ Every parameter gets one catalog card, its value and baseline chosen by a rule (`src/forgegraph/catalog_build/cards.py`), so a new parameter needs no card of its own; the page shows the variable parameters' cards, and the perturbation test draws them all. It needs a baseline that shows it only if none does: the build (for a variable parameter) or the perturbation test (for any) names a parameter that changes no baseline's drawing, and the fix is a generic scenario added to the baselines in the matching catalog file under `src/forgegraph/specifications/catalogs/` (or the pack's `catalogs/`).
23
23
 
24
- Rebuild the catalogs (it keeps its review comments; Node 18+ is needed once it has some), and the image baselines if graphs look different on purpose:
24
+ Rebuild the catalog (it keeps its review comments; Node 18+ is needed once it has some), the mirrors, and the image baselines if graphs look different on purpose:
25
25
 
26
26
  ```bash
27
27
  uv run forgegraph catalog build
28
- uv run forgegraph catalog build --pack forgegraph_base_2
29
28
  uv run python scripts/mirror_specs.py
30
29
  uv run python scripts/build_recreations.py --baselines
31
30
  ```
@@ -38,8 +37,8 @@ Open a GitHub issue describing the graph you need, ideally with an example image
38
37
 
39
38
  ## Releasing
40
39
 
41
- 1. Set the new version in pyproject.toml and in each built-in pack's pack.yaml.
42
- 2. Rebuild the catalogs and mirrors (above).
40
+ 1. Set the new version in pyproject.toml and in the built-in pack's pack.yaml, and for a new minor version, its `requires` (`">=0.4, <0.5"` for 0.4).
41
+ 2. Rebuild the catalog and mirrors (above).
43
42
  3. Write the visual changelog, `uv run python scripts/visual_changelog.py v<last version>`, and the CHANGELOG entry, linking it.
44
43
  4. Merge with CI green, and tag the merge commit `v<version>`.
45
44
  5. Run the Publish workflow.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: forgegraph
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Fast, consistent, publication-quality graphs from pandas DataFrames, usable by people and AI agents.
5
5
  Project-URL: Repository, https://github.com/sskaye/forgegraph
6
6
  Project-URL: Issues, https://github.com/sskaye/forgegraph/issues
@@ -106,12 +106,11 @@ name: voltage
106
106
  version: 0
107
107
  description: Cell voltage over a test, one line per cell.
108
108
  extends: None
109
- fixed_parameters: None
110
- variable_parameters:
109
+ parameters: !var
111
110
  specifications:
112
111
  graph_type: xy
113
- theme: forgegraph_base/base_theme
114
- format: forgegraph_base/base_format
112
+ theme: forgegraph_default/light_theme
113
+ format: forgegraph_default/compact_format
115
114
  text:
116
115
  title: Cell voltage
117
116
  data:
@@ -121,12 +120,9 @@ variable_parameters:
121
120
  x:
122
121
  data:
123
122
  column: hours
124
- y:
125
- data:
126
- column: voltage
127
- encoding:
128
- color:
129
- column: cell
123
+ series:
124
+ voltage:
125
+ color: cell
130
126
  ```
131
127
 
132
128
  ```bash
@@ -135,7 +131,7 @@ forgegraph render voltage.yaml
135
131
 
136
132
  ## Packs and configs
137
133
 
138
- Every named theme, format, GraphSpec, layout and schedule comes from a **pack**, a folder or pip package of specs, and is named `pack/name`. The initial built-in packs are `forgegraph_base` and `forgegraph_base_2`, stored in src/forgegraph/packs.
134
+ Every named theme, format, GraphSpec, layout and schedule comes from a **pack**, a folder or pip package of specs, and is named `pack/name`. ForgeGraph's built-in pack, `forgegraph_default`, is stored in src/forgegraph/packs: its theme, `light_theme`, its formats, `compact_format` for single graphs and `wide_format` for layouts, and a layout and a schedule to extend.
139
135
 
140
136
  A **config** lists the packs that may be used:
141
137
 
@@ -163,10 +159,9 @@ Every command takes `--help`, and `--json` for agents.
163
159
 
164
160
  ## Catalogs
165
161
 
166
- Each pack in this repository has a catalog: a page with a card for each parameter, showing what changing it does. Download a page and open it in a browser.
162
+ The built-in pack has a catalog: a page with a card for each parameter, showing what changing it does. Download it and open it in a browser.
167
163
 
168
- - [forgegraph_base](https://github.com/sskaye/forgegraph/blob/main/catalog/forgegraph_base.html): ForgeGraph's own pack, with its graph types and layer types.
169
- - [forgegraph_base_2](https://github.com/sskaye/forgegraph/blob/main/catalog/forgegraph_base_2.html): its own theme and format, and the same graph types and layer types drawn in them.
164
+ - [forgegraph_default](https://github.com/sskaye/forgegraph/blob/main/catalog/forgegraph_default.html): its theme and formats, and ForgeGraph's graph types and layer types drawn in them.
170
165
 
171
166
  ## Your graphs are yours
172
167
 
@@ -174,7 +169,8 @@ ForgeGraph is [MIT-licensed](https://github.com/sskaye/forgegraph/blob/main/LICE
174
169
 
175
170
  ## Documentation
176
171
 
177
- - [Specifications](https://github.com/sskaye/forgegraph/blob/main/docs/specs): every spec, a folder per kind.
172
+ - [Specifications](https://github.com/sskaye/forgegraph/blob/main/docs/specifications): the file that defines each kind of spec, every parameter with its default, range and description.
173
+ - [Built-in pack](https://github.com/sskaye/forgegraph/blob/main/docs/packs/forgegraph_default): forgegraph_default's specs.
178
174
  - [Changelog](https://github.com/sskaye/forgegraph/blob/main/CHANGELOG.md).
179
175
  - [Contributing](https://github.com/sskaye/forgegraph/blob/main/CONTRIBUTING.md): working on ForgeGraph itself.
180
176
  - Design record, in [docs/design](https://github.com/sskaye/forgegraph/blob/main/docs/design):
@@ -192,12 +188,13 @@ Feature requests and bug reports go to [GitHub issues](https://github.com/sskaye
192
188
  - **cli/**: the `forgegraph` command.
193
189
  - **spec/**: reading, checking and resolving specs, and the provenance record.
194
190
  - **drawing/**: drawing, layout and lint. **layouts/**: `fg.layout` and layouts. **jobs/**: scheduled renders.
195
- - **packs/**: packs and configs, and the built-in packs `forgegraph_base` and `forgegraph_base_2`. **fonts/**: bundled fonts and their licences.
196
- - **graph_types/**, **layers/**: the graph type and layer type definitions. **catalog_build/**: the catalog builder. **catalogs/**: each definition's catalog file.
191
+ - **specifications/**: the specification files, which define each kind of spec, and the graph types' and layer types' catalog files.
192
+ - **packs/**: packs and configs, and the built-in pack `forgegraph_default`. **fonts/**: bundled fonts and their licences.
193
+ - **catalog_build/**: the catalog builder.
197
194
  - **skill/forgegraph/**: the agent skill. `forgemark.py`: Forgemark's review-comment format, and its pinned tool.
198
195
  - **catalog/**: the generated catalog pages.
199
196
  - **examples/**: sample data; `packs/acme`, an example pack in a deliberately different style; and `layouts/`, a paper figure and a dashboard.
200
197
  - **evals/skill/**: the skill's eval cases, run by `scripts/run_skill_evals.py`.
201
198
  - **tests/**, **scripts/**: tests, and build and maintenance scripts.
202
- - **docs/**: the specifications, the design record and the visual changelogs. **review/**: each build phase's review pages.
199
+ - **docs/**: the specifications' and packs' pages, the design record and the visual changelogs. **review/**: each build phase's review pages.
203
200
 
@@ -76,12 +76,11 @@ name: voltage
76
76
  version: 0
77
77
  description: Cell voltage over a test, one line per cell.
78
78
  extends: None
79
- fixed_parameters: None
80
- variable_parameters:
79
+ parameters: !var
81
80
  specifications:
82
81
  graph_type: xy
83
- theme: forgegraph_base/base_theme
84
- format: forgegraph_base/base_format
82
+ theme: forgegraph_default/light_theme
83
+ format: forgegraph_default/compact_format
85
84
  text:
86
85
  title: Cell voltage
87
86
  data:
@@ -91,12 +90,9 @@ variable_parameters:
91
90
  x:
92
91
  data:
93
92
  column: hours
94
- y:
95
- data:
96
- column: voltage
97
- encoding:
98
- color:
99
- column: cell
93
+ series:
94
+ voltage:
95
+ color: cell
100
96
  ```
101
97
 
102
98
  ```bash
@@ -105,7 +101,7 @@ forgegraph render voltage.yaml
105
101
 
106
102
  ## Packs and configs
107
103
 
108
- Every named theme, format, GraphSpec, layout and schedule comes from a **pack**, a folder or pip package of specs, and is named `pack/name`. The initial built-in packs are `forgegraph_base` and `forgegraph_base_2`, stored in src/forgegraph/packs.
104
+ Every named theme, format, GraphSpec, layout and schedule comes from a **pack**, a folder or pip package of specs, and is named `pack/name`. ForgeGraph's built-in pack, `forgegraph_default`, is stored in src/forgegraph/packs: its theme, `light_theme`, its formats, `compact_format` for single graphs and `wide_format` for layouts, and a layout and a schedule to extend.
109
105
 
110
106
  A **config** lists the packs that may be used:
111
107
 
@@ -133,10 +129,9 @@ Every command takes `--help`, and `--json` for agents.
133
129
 
134
130
  ## Catalogs
135
131
 
136
- Each pack in this repository has a catalog: a page with a card for each parameter, showing what changing it does. Download a page and open it in a browser.
132
+ The built-in pack has a catalog: a page with a card for each parameter, showing what changing it does. Download it and open it in a browser.
137
133
 
138
- - [forgegraph_base](https://github.com/sskaye/forgegraph/blob/main/catalog/forgegraph_base.html): ForgeGraph's own pack, with its graph types and layer types.
139
- - [forgegraph_base_2](https://github.com/sskaye/forgegraph/blob/main/catalog/forgegraph_base_2.html): its own theme and format, and the same graph types and layer types drawn in them.
134
+ - [forgegraph_default](https://github.com/sskaye/forgegraph/blob/main/catalog/forgegraph_default.html): its theme and formats, and ForgeGraph's graph types and layer types drawn in them.
140
135
 
141
136
  ## Your graphs are yours
142
137
 
@@ -144,7 +139,8 @@ ForgeGraph is [MIT-licensed](https://github.com/sskaye/forgegraph/blob/main/LICE
144
139
 
145
140
  ## Documentation
146
141
 
147
- - [Specifications](https://github.com/sskaye/forgegraph/blob/main/docs/specs): every spec, a folder per kind.
142
+ - [Specifications](https://github.com/sskaye/forgegraph/blob/main/docs/specifications): the file that defines each kind of spec, every parameter with its default, range and description.
143
+ - [Built-in pack](https://github.com/sskaye/forgegraph/blob/main/docs/packs/forgegraph_default): forgegraph_default's specs.
148
144
  - [Changelog](https://github.com/sskaye/forgegraph/blob/main/CHANGELOG.md).
149
145
  - [Contributing](https://github.com/sskaye/forgegraph/blob/main/CONTRIBUTING.md): working on ForgeGraph itself.
150
146
  - Design record, in [docs/design](https://github.com/sskaye/forgegraph/blob/main/docs/design):
@@ -162,12 +158,13 @@ Feature requests and bug reports go to [GitHub issues](https://github.com/sskaye
162
158
  - **cli/**: the `forgegraph` command.
163
159
  - **spec/**: reading, checking and resolving specs, and the provenance record.
164
160
  - **drawing/**: drawing, layout and lint. **layouts/**: `fg.layout` and layouts. **jobs/**: scheduled renders.
165
- - **packs/**: packs and configs, and the built-in packs `forgegraph_base` and `forgegraph_base_2`. **fonts/**: bundled fonts and their licences.
166
- - **graph_types/**, **layers/**: the graph type and layer type definitions. **catalog_build/**: the catalog builder. **catalogs/**: each definition's catalog file.
161
+ - **specifications/**: the specification files, which define each kind of spec, and the graph types' and layer types' catalog files.
162
+ - **packs/**: packs and configs, and the built-in pack `forgegraph_default`. **fonts/**: bundled fonts and their licences.
163
+ - **catalog_build/**: the catalog builder.
167
164
  - **skill/forgegraph/**: the agent skill. `forgemark.py`: Forgemark's review-comment format, and its pinned tool.
168
165
  - **catalog/**: the generated catalog pages.
169
166
  - **examples/**: sample data; `packs/acme`, an example pack in a deliberately different style; and `layouts/`, a paper figure and a dashboard.
170
167
  - **evals/skill/**: the skill's eval cases, run by `scripts/run_skill_evals.py`.
171
168
  - **tests/**, **scripts/**: tests, and build and maintenance scripts.
172
- - **docs/**: the specifications, the design record and the visual changelogs. **review/**: each build phase's review pages.
169
+ - **docs/**: the specifications' and packs' pages, the design record and the visual changelogs. **review/**: each build phase's review pages.
173
170
 
@@ -31,10 +31,10 @@ Also:
31
31
  | Area | Decision |
32
32
  | --- | --- |
33
33
  | Renderer (D1) | matplotlib only; no interactivity in the MVP |
34
- | Core API (D2) | Spec-first: `render(spec)`; Python functions build specs. The definition files are the source; `forgegraph schema`, the catalog pages and the docs/specs mirrors are generated from them |
35
- | Style (D3) | Theme × format, layered and explainable; themes hold fixed parameters (graphs can't change them) and variable parameters with defaults (graphs can change them) |
34
+ | Core API (D2) | Spec-first: `render(spec)`; Python functions build specs. The definition files are the source; `forgegraph schema`, the catalog pages and the docs/specifications mirrors are generated from them |
35
+ | Style (D3) | Theme × format, layered and explainable; themes tag parameters !fix (graphs can't change them) or !var, with defaults (graphs can change them) |
36
36
  | Extension (D4) | Packs, data only, are the only source of named specs; the main and folder configs say which may be used, and a pack only a folder config lists is non-standard; graph types and layers stay in the core |
37
- | Graph types (D5) | Each graph type's and layer type's definition file (graph_types/xy.yaml, layers/<type>.yaml) is the source of truth |
37
+ | Graph types (D5) | Each graph type's and layer type's definition file (specifications/graphtype_xy.yaml, specifications/layer_<type>.yaml) is the source of truth |
38
38
  | Review (D6) | Tabbed HTML catalog with Forgemark comments |
39
39
  | Input (D7) | A pandas DataFrame, from the Python API or a Parquet file named by the GraphSpec; an optional reference to the code that made it |
40
40
  | Labels (D8) | Lightweight sub/superscript markup, applied to explicit labels only |
@@ -54,7 +54,7 @@ Also:
54
54
 
55
55
  1. **The GraphSpec is the product.** Skill, notebooks, CLI, scheduler, dashboards and the future desktop app all produce or consume the same document. Python function calls build GraphSpecs; they are not a second code path.
56
56
  2. **Three tiers of control, no fourth.**
57
- - **Fixed style**: house style, set by the theme and format (by the org, journal or user), never by an individual graph. Each value is a **parameter**, e.g. `plot_area.sides` or `series.colors`; a theme may also leave some parameters variable, for graphs to change.
57
+ - **Fixed style**: house style, set by the theme and format (by the org, journal or user), never by an individual graph. Each value is a **parameter**, e.g. `plot_area.border.sides` or `series.colors`; a theme may also leave some parameters variable, for graphs to change.
58
58
  - **Options**: what an individual graph may vary. Each is documented with a default, except the data itself (the data file and the required mappings such as `x` and `y`), which every graph must specify.
59
59
  - **Escape hatch**: raw matplotlib on a themed figure, always marked non-standard.
60
60
  3. **One source per fact; everything else is generated.** Each kind of document is defined by its definition file (§8): every parameter with its default, range and description. The code reads them; `forgegraph schema`, the catalog pages and the docs/specs mirrors are generated from them, and the skill reads them at run time. Nothing generated is edited by hand.
@@ -70,8 +70,8 @@ Also:
70
70
  | Term | What it is | Where it lives |
71
71
  | --- | --- | --- |
72
72
  | **GraphSpec** | One specific graph: data, graph type, column mappings, parameters, layers | a YAML file, embedded in the saved graph |
73
- | **Graph type** | A kind of graph (`xy`): the parameters a GraphSpec chooses, in its definition file, drawn by its module in drawing/ | `src/forgegraph/graph_types/<type>.yaml` (core only) |
74
- | **Layer** | An addition drawn on a graph (`reference_line`, `note`, `error_bar`), defined by its own file | `src/forgegraph/layers/<type>.yaml` (core only) |
73
+ | **Graph type** | A kind of graph (`xy`): the parameters a GraphSpec chooses, in its definition file, drawn by its module in drawing/ | `src/forgegraph/specifications/graphtype_<type>.yaml` (core only) |
74
+ | **Layer** | An addition drawn on a graph (`reference`, `note`, `error_bar`), defined by its own file | `src/forgegraph/specifications/layer_<type>.yaml` (core only) |
75
75
  | **GraphSpec to extend** | A GraphSpec without data, reused by others through `extends:`. E.g. "polarization curve" = `xy` + its columns + axis titles | a pack's `graphspecs/` |
76
76
  | **Theme** | The look: fixed style (fonts, palettes, spines, ticks, legend style), plus defaults for options | a pack's `themes/` |
77
77
  | **Format** | The destination: figure size, base font size, file types, dpi, provenance level | a pack's `formats/` |
@@ -106,19 +106,18 @@ Also:
106
106
 
107
107
  ### 3.1 What it looks like to use
108
108
 
109
- **GraphSpec** (docs/specs/graphspec/polarization.md lists every parameter):
109
+ **GraphSpec** (docs/specifications/graphspec_example.md lists every parameter):
110
110
 
111
111
  ```yaml
112
112
  name: voltage
113
113
  version: 0
114
114
  description: Cell voltage over a test, one line per cell.
115
115
  extends: None
116
- fixed_parameters: None
117
- variable_parameters:
116
+ parameters: !var
118
117
  specifications:
119
118
  graph_type: xy
120
- theme: forgegraph_base/base_theme # from a pack the configs allow
121
- format: forgegraph_base/base_format
119
+ theme: forgegraph_default/light_theme # from a pack the configs allow
120
+ format: forgegraph_default/compact_format
122
121
  text:
123
122
  title: Cell voltage
124
123
  data:
@@ -128,18 +127,17 @@ variable_parameters:
128
127
  x:
129
128
  data:
130
129
  column: hours
131
- y:
132
- data:
133
- column: voltage
134
- encoding:
135
- color: cell
130
+ series:
131
+ voltage:
132
+ color: cell
136
133
  layers:
137
134
  target:
138
- type: reference_line
135
+ type: reference
139
136
  axis: y
140
- value: 0.7
141
- label:
142
- text: Target
137
+ line_1:
138
+ value: 0.7
139
+ label:
140
+ text: Target
143
141
  ```
144
142
 
145
143
  **Python:**
@@ -190,16 +188,18 @@ Interactivity is not part of the MVP. If it becomes a requirement later, the opt
190
188
 
191
189
  ### D3. Style resolution: theme × format, layered, explainable
192
190
 
193
- **One structure for every specification.** Themes, formats, graph types and GraphSpecs are written the same way, and each kind is defined by one file: the base pack's `themes/base_theme.yaml` and `formats/base_format.yaml` (in `src/forgegraph/packs/forgegraph_base/`), each graph type's own file (`graph_types/xy.yaml`) and an example GraphSpec that sets every parameter (the base pack's `graphspecs/polarization.yaml`). Every file has a header (`name`, `version`, `description`, `extends`) and two sections, `fixed_parameters` and `variable_parameters`, each holding categories of parameters. In a definition file every line, header included, gives its value and a comment `[RANGE]; DESCRIPTION`, and the code reads every definition, range included, from those files.
191
+ **One structure for every specification.** Themes, formats, graph types and GraphSpecs are written the same way, and each kind is defined by one file in `src/forgegraph/specifications/`: `theme.yaml`, `format.yaml`, each graph type's own file (`graphtype_xy.yaml`) and an example GraphSpec (`graphspec_example.yaml`). A specification file only defines its kind: no spec can name or extend it, so it lists every parameter without having to be a usable look; the built-in pack, forgegraph_default, holds the specs graphs are drawn with. Every file has a header (`name`, `version`, `description`, `extends`) and `parameters`, one tree of categories of parameters. In a definition file every line, header included, gives its value and a comment `[RANGE]; DESCRIPTION`, and the code reads every definition, range included, from those files.
194
192
 
195
- - **Fixed:** a GraphSpec can't change it (principle 2). A different look needs a different theme, typically one that extends the house theme.
196
- - **Variable:** a GraphSpec may change it, under the same category; the file gives the value it has otherwise.
197
- - **extends:** every parameter a file doesn't list is inherited, fixed or variable as it was. With `extends: None` the file lists every parameter.
193
+ - **!fix:** a GraphSpec can't change it (principle 2). A different look needs a different theme, typically one that extends the house theme.
194
+ - **!var:** a GraphSpec may change it, under the same category; the file gives the value it has otherwise.
195
+ - A tag goes before a value (`width: !var 0.15`) or on a category (`output: !var`), where it applies to everything inside unless a tag further in says otherwise. A tag with no value (`width: !var`) changes only the status.
196
+ - **extends:** every parameter a file doesn't list is inherited, with its status. The nearest file that tags a parameter, or a category above it, decides its status. With `extends: None` the file lists every parameter and tags each, itself or through a category.
197
+ - **Styles:** in a theme, a category marked `[text style]`, `[line style]` or `[series style]` has the parameters of `text`, `line` or `series`, and takes their values unless it, or a file nearer in the extends chain, sets its own; a GraphSpec's settings of them carry through too. A member that no file tags, itself or through a category inside `parameters`, has its general parameter's status, so a fixed `text.font` fixes every text style's font. A style's category leaves out the members what it draws has no use for (a tick mark has no corners), listed in spec/parameters.py, so setting one is an error.
198
198
  - In a theme or format, a graph type's name used as a heading holds parameters for that graph type only.
199
199
 
200
200
  Every size in a theme is a multiple of the format's base font ("tick labels = 0.9 × base"), so a theme renders correctly in any format. That matters because graphs go to many destinations, not mainly slides (so no `.pptx` export: the examples were collected on slides only for convenience).
201
201
 
202
- **The base theme** (forgegraph_base/base_theme): Roboto, a full box, outward ticks, the Okabe–Ito palette, a frameless key, direct labels where they fit. The `acme` example pack has a *deliberately different* house style (IBM Plex Serif, two sides of the box, a grid, Paul Tol's muted palette), so it shows that a pack can change the look completely.
202
+ **The default theme** (forgegraph_default/light_theme): Roboto, semibold titles, a full box, outward ticks, the Okabe–Ito palette with a grey benchmark, a frameless key, direct labels where they fit. It fixes the font, the palette and its lists, the ticks' direction and algorithms, the grid and the series' lines. The `acme` example pack has a *deliberately different* house style (IBM Plex Serif, two sides of the box, a grid, Paul Tol's muted palette), so it shows that a pack can change the look completely.
203
203
 
204
204
  **Fonts and PDFs:**
205
205
 
@@ -223,7 +223,7 @@ acme-forgegraph/
223
223
  └── fonts/
224
224
  ```
225
225
 
226
- - **Packs are the only source of named specs.** Every theme, format, GraphSpec, layout or schedule that a file names, under specifications or in extends, comes from a pack and is named `pack/name`. ForgeGraph's own specs are the pack `forgegraph_base`: `base_theme`, `base_format`, `base_layout_format`, `base_layout`, `base_schedule` and example GraphSpecs. Specs of your own are a folder pack. The files you draw (GraphSpecs, layouts and schedules, a layout's tiles and a schedule's targets) are given by path.
226
+ - **Packs are the only source of named specs.** Every theme, format, GraphSpec, layout or schedule that a file names, under specifications or in extends, comes from a pack and is named `pack/name`. ForgeGraph's own specs are the pack `forgegraph_default`: `light_theme`, `compact_format`, `wide_format`, `layout`, `schedule` and an example GraphSpec. Specs of your own are a folder pack. The files you draw (GraphSpecs, layouts and schedules, a layout's tiles and a schedule's targets) are given by path.
227
227
  - **Graph types and layer types** are ForgeGraph's code, not pack data, so they're named without a prefix (`xy`) and can always be used.
228
228
  - **The config says which packs may be used** (packs/__init__.py has the rules): the main config for all work, and a folder config, `forgegraph.toml`, for the files below it.
229
229
  - A pack only a folder config lists is non-standard: it works, but whatever is drawn with it is watermarked and its record names the pack. That way someone who can't change an organization's main config can still make and try a pack, and nothing drawn with it passes as standard.
@@ -241,7 +241,7 @@ acme-forgegraph/
241
241
 
242
242
  ### D5. Graph types and layer types: one definition file each
243
243
 
244
- A graph type is defined by its own file in `src/forgegraph/graph_types/`, starting with `xy.yaml`, and drawn by drawing/xy.py; a layer type by its file in `src/forgegraph/layers/`, drawn by drawing/layers.py. Its parameters are what a GraphSpec chooses for one graph: the data column for each axis, the encodings, the marks and the legend. Things drawn on top of the graph, including those computed from its data (error bars, a reference line at the mean, a fitted curve), are layers. A parameter defined as None with no None in its range is required: a GraphSpec must set it. Style values never live in a graph type: the renderer reads them from the theme, so the theme stays the single place where style values live. The graph code receives a DataFrame; loading and preparing data happens before it.
244
+ A graph type is defined by its own file in `src/forgegraph/specifications/`, starting with `graphtype_xy.yaml`, and drawn by drawing/xy.py; a layer type by its file there (`layer_<type>.yaml`), drawn by drawing/layers.py. A category's brackets say what the code needs to know about it, from a fixed set: its style, `same as <sibling>` for a category with a sibling's parameters, and `look from <theme category>` for a layer category whose look comes from the theme. Descriptions are for people, and no code reads them. Its parameters are what a GraphSpec chooses for one graph: the data column for each axis, the encodings, the marks and the legend. Things drawn on top of the graph, including those computed from its data (error bars, a reference line at the mean, a fitted curve), are layers. A parameter defined as None with no None in its range is required: a GraphSpec must set it. Style values never live in a graph type: the renderer reads them from the theme, so the theme stays the single place where style values live. The graph code receives a DataFrame; loading and preparing data happens before it.
245
245
 
246
246
  **Series labels come from a set of labelling methods.** Each method either places every label cleanly or reports that it can't. The theme lists the methods it allows, in order of preference, and a graph's `legend.method: auto` uses the first that fits. A method chosen by name that doesn't fit, or `auto` with nothing that fits, is an error that says what to change: ForgeGraph never draws labels over data as a fallback. The first methods are:
247
247
 
@@ -249,9 +249,9 @@ A graph type is defined by its own file in `src/forgegraph/graph_types/`, starti
249
249
  - `key`: a key at the first of the GraphSpec's legend.key.positions that covers no data, or outside the plot area (legend.key.inside: false);
250
250
  - `none`.
251
251
 
252
- A single series is named too, unless its name is the y-axis title's text, which already names it. A numeric `encoding.color.column` is drawn on a color scale with a color bar, or with palette colors and labels when `encoding.color.type` is `categorical`; with lines shown, a line joins only points of one color.
252
+ A single series is named too, unless its name is the y-axis title's text, which already names it. A series colored by a numeric column is drawn on a color scale with a color bar, or with palette colors and labels when its `color_scale` is `categorical`; with lines shown, a line joins only points of one color.
253
253
 
254
- Placement is deterministic. A direct label sits inside the plot, beside its own series, touching nothing; drawing/labels.py lists the rules. A key, and the style key that explains encoding.line.style.column, sits inside the plot area at the first position that covers no data, or outside it, with the figure laid out again around it; drawing/key.py places it. Existing labelling libraries (matplotlib-label-lines, adjustText) don't meet these rules.
254
+ Placement is deterministic. A direct label sits inside the plot, beside its own series, touching nothing; drawing/labels.py lists the rules. A key, and the style key that explains line styles set by a column, sits inside the plot area at the first position that covers no data, or outside it, with the figure laid out again around it; drawing/key.py places it. Existing labelling libraries (matplotlib-label-lines, adjustText) don't meet these rules.
255
255
 
256
256
  ### D6. Review surface: tabbed HTML catalog, Forgemark comments
257
257
 
@@ -322,9 +322,8 @@ When a request isn't in the catalog, the agent says so plainly, then:
322
322
  name: lab_throughput
323
323
  version: 0
324
324
  description: Daily operations view of lab throughput.
325
- extends: forgegraph_base/base_layout
326
- fixed_parameters: None
327
- variable_parameters:
325
+ extends: forgegraph_default/layout
326
+ parameters:
328
327
  specifications:
329
328
  format: ops/dashboard
330
329
  separators:
@@ -352,9 +351,9 @@ variable_parameters:
352
351
  icp_oes_01: runs_icp_oes_01.yaml
353
352
  ```
354
353
 
355
- **A layout arranges GraphSpecs.** A layout file (`*.layout.yaml`, docs/specs/layout/base_layout.md) holds only what's particular to several graphs together: the arrangement, and what separates the graphs. Text, colors, fonts and the look of each graph stay in each tile's GraphSpec and its theme, so a tile drawn alone and in a layout is the same graph. A layout has no per-tile settings and no text of its own.
354
+ **A layout arranges GraphSpecs.** A layout file (`*.layout.yaml`, docs/specifications/layout_example.md) holds only what's particular to several graphs together: the arrangement, and what separates the graphs. Text, colors, fonts and the look of each graph stay in each tile's GraphSpec and its theme, so a tile drawn alone and in a layout is the same graph. A layout has no per-tile settings and no text of its own.
356
355
 
357
- - **Format, no theme.** The layout's format (by default the base pack's base_layout_format: the base format at 300 mm) sets the size and output of the whole figure, and replaces each tile's own format, so every tile is drawn at one base size. The layout can set the format's variable parameters, such as its file types. Each tile keeps its own theme.
356
+ - **Format, no theme.** The layout's format (by default the default pack's wide_format: the compact format at 300 mm) sets the size and output of the whole figure, and replaces each tile's own format, so every tile is drawn at one base size. The layout can set the format's variable parameters, such as its file types. Each tile keeps its own theme.
358
357
  - **The grid.** grid.areas draws the grid as rows of tile names, as CSS `grid-template-areas`, matplotlib's `subplot_mosaic` and patchwork do: a name repeated over a rectangle of cells spans it, and `.` is a blank cell. column_widths and row_heights are ratios of plot boxes.
359
358
  - **Aligned plot boxes.** A tab's tiles are laid out together, as patchwork, egg and cowplot do: each column's and row's margins are the most room any of its tiles needs around its plot box, measured to the letters, so plot boxes line up across tiles whatever their tick labels. A spanning tile's plot box runs across the margins and gaps inside it. With the format's height auto, each row is as tall as its tiles' aspects need. Tiles are never shrunk to fit; a figure too small for them is an error naming them.
360
359
  - **Separators.** The gap between tiles, its color, and optionally a line along the middle of each gap between two tiles.
@@ -368,7 +367,7 @@ Paper figures and ops dashboards differ only in their format and defaults; examp
368
367
 
369
368
  ### D12. Scheduled graphs and dashboards
370
369
 
371
- A scheduled job is a **schedule file** (`*.schedule.yaml`, docs/specs/schedule/base_schedule.md): the GraphSpec and layout files to render, where to save them, and when. It extends the base schedule like any other spec, so it can be reviewed, kept in version control and checked like any parameter.
370
+ A scheduled job is a **schedule file** (`*.schedule.yaml`, docs/specifications/schedule.md): the GraphSpec and layout files to render, where to save them, and when. It extends the default pack's schedule like any other spec, so it can be reviewed, kept in version control and checked like any parameter.
372
371
 
373
372
  - **Cadence.** `interval.count` counts `interval.unit` (minute, hour, day, week, month or year), and `at` says when in each unit, one time or a list: ":30", "06:00", "mon 06:00", "1 06:00", "Jan 1 06:00". `interval.count` divides 60, 24 and 12 for minutes, hours and months, and is 1 otherwise, so every form maps exactly onto every scheduler. Times are the machine's local time.
374
373
  - **`forgegraph job run lab.schedule.yaml`** runs a schedule once. Whatever the environment offers schedules that command: the machine's scheduler (launchd, systemd, cron, Task Scheduler), a GitHub Actions workflow, a Claude scheduled task. ForgeGraph writes no scheduler entries itself, so every user's path is the same.
@@ -382,7 +381,7 @@ A scheduled job is a **schedule file** (`*.schedule.yaml`, docs/specs/schedule/b
382
381
 
383
382
  ### D13. Provenance and reproducibility
384
383
 
385
- Every output embeds a provenance record, at its format's provenance.level (full, minimal or off), so a graph can be traced and redrawn; docs/specs/provenance/provenance.md lists what each level records.
384
+ Every output embeds a provenance record, at its format's provenance.level (full, minimal or off), so a graph can be traced and redrawn; docs/specifications/provenance.md lists what each level records.
386
385
 
387
386
  - **Carriers.** Provenance is one JSON record written through matplotlib's own metadata support: a PNG text chunk, a JPEG comment, the TIFF image description, the SVG Dublin Core `Description` field, and the PDF Info dictionary. HTML pages carry it in `<script type="application/json">`. drawing/export.py writes it.
388
387
  - **Layouts.** A layout's files record the layout as written and, for each tab they show, each tile's GraphSpec file and the same entries as a GraphSpec's.
@@ -440,13 +439,13 @@ Every output embeds a provenance record, at its format's provenance.level (full,
440
439
 
441
440
  **Graph types:** `xy` draws lines, markers and bars: grouped, stacked, 100 % stacked and horizontal, with value labels.
442
441
 
443
- **Layers:** `reference_line` (horizontal or vertical, labelled), `note` (the "conditions" text block) and `error_bar`. Error bars are a layer, drawn from DataFrame columns or the spread of aggregated rows: the specification names their absence as a failing of Tableau.
442
+ **Layers:** `reference` (horizontal or vertical lines, labelled, or a band between two; at a value, an aggregate, or following the data), `note` (the "conditions" text block) and `error_bar`. Error bars are a layer, drawn from DataFrame columns or the spread of aggregated rows: the specification names their absence as a failing of Tableau.
444
443
 
445
444
  | Capability | Status |
446
445
  | --- | --- |
447
446
  | lines and markers, color by a column, direct labels, a benchmark series | built |
448
447
  | markers-only scatter with many points | built |
449
- | replicates: several lines per color group (encoding.detail.column) | built |
448
+ | replicates: several lines per color group (a series' split) | built |
450
449
  | line style by a column, with a key for the styles | built |
451
450
  | log axes, unit markup | built |
452
451
  | color by a numeric column, with a color bar | built |
@@ -494,20 +493,21 @@ Repo `sskaye/forgegraph`; distribution, import and CLI `forgegraph`. The README'
494
493
 
495
494
  ## 8. Specifications
496
495
 
497
- Each kind of document is specified by its definition file: every parameter with its default, range and description. The code reads it, and tests check the code against it, including the edges of every range. docs/specs/ mirrors each file for review.
496
+ Each kind of document is specified by its definition file: every parameter with its default, range and description. The code reads it, and tests check the code against it, including the edges of every range. docs/specifications/ mirrors each file for review, and docs/packs/ each built-in pack's.
498
497
 
499
498
  | Specification | Definition file |
500
499
  | --- | --- |
501
- | **Theme** | src/forgegraph/packs/forgegraph_base/themes/base_theme.yaml |
502
- | **Format** | src/forgegraph/packs/forgegraph_base/formats/base_format.yaml |
503
- | **Graph type** | src/forgegraph/graph_types/xy.yaml |
504
- | **GraphSpec** | src/forgegraph/packs/forgegraph_base/graphspecs/polarization.yaml |
505
- | **Layer type** | src/forgegraph/layers/, a file per type |
506
- | **Layout** | src/forgegraph/packs/forgegraph_base/layouts/base_layout.yaml |
507
- | **Schedule** | src/forgegraph/packs/forgegraph_base/schedules/base_schedule.yaml |
508
- | **Pack and config** | src/forgegraph/packs/forgegraph_base/pack.yaml, src/forgegraph/packs/config.toml |
509
- | **Provenance record** | src/forgegraph/spec/provenance.yaml |
510
- | **Catalog** | src/forgegraph/catalogs/, and each pack's catalogs/ |
500
+ | **Theme** | src/forgegraph/specifications/theme.yaml |
501
+ | **Format** | src/forgegraph/specifications/format.yaml |
502
+ | **Graph type** | src/forgegraph/specifications/graphtype_xy.yaml |
503
+ | **GraphSpec** | src/forgegraph/specifications/graphspec_example.yaml |
504
+ | **Layer type** | src/forgegraph/specifications/layer_<type>.yaml, a file per type |
505
+ | **Layout** | src/forgegraph/specifications/layout_example.yaml |
506
+ | **Schedule** | src/forgegraph/specifications/schedule.yaml |
507
+ | **Pack and config** | src/forgegraph/specifications/pack.yaml, src/forgegraph/specifications/config.toml |
508
+ | **Provenance record** | src/forgegraph/specifications/provenance.yaml |
509
+ | **Styles** | src/forgegraph/specifications/styles.yaml |
510
+ | **Catalog** | src/forgegraph/specifications/catalogs/, and each pack's catalogs/ |
511
511
 
512
512
  ---
513
513
 
@@ -574,12 +574,11 @@ Decisions made while building whose reasons aren't given above.
574
574
  - **The layout always fits its tick labels.** Tick labels can keep a layout from settling: a taller plot box gets more, wider labels, which make it shorter again. After 6 unsettled passes it lays out once more with, on each side, the most room either of the last two passes needed, so whichever labels the final draw has fit.
575
575
  - **Date axes use one calendar unit per axis.** matplotlib's automatic date ticks mix units (the 1st and 15th, labelled "Aug" and "15") and name the shared context in a corner. As in d3 and Vega-Lite, ticks are a calendar unit's boundaries, never finer than the data's spacing, with the next unit up on a second line where it changes, as Plotly and Datawrapper do. Weekly ticks fall on Mondays even when the data starts on another day, which reads more naturally than counting from the data's first day. ForgeGraph writes %-d, %-m and %-H itself, since strftime's differ by platform.
576
576
  - **The default palette is Okabe–Ito.** With 4 or more groups, color_blind's colors ran together in the skill evals: two blues and two greys. Okabe and Ito's palette keeps its first four colors distinct for color-blind readers.
577
- - **The base theme leaves line widths and marker sizes to the GraphSpec.** Agents in the skill evals needed smaller markers and thinner lines for dense data. Key samples have their own marker size, so shrinking the plot's markers leaves the key readable.
578
- - **forgegraph_base_2 follows matplotlib's output, not its style sheet's numbers.** matplotlib draws the style's font weight 350 with Source Sans 3 Light, so the theme uses light. Its gaps were measured from matplotlib's output and set to match, since matplotlib measures from text boxes and ForgeGraph from letters.
577
+ - **The default theme leaves marker sizes and opacity to the GraphSpec.** Agents in the skill evals needed smaller markers for dense data. Key samples have their own marker size, so shrinking the plot's markers leaves the key readable.
579
578
 
580
579
  ### Names
581
580
 
582
- - **Parameter names follow Vega-Lite where a standard term exists** (encoding, mark, detail), keeping ForgeGraph's nesting, snake_case and units. A third of the parameters have no Vega-Lite equivalent, so its names weren't adopted wholesale. "Legend" is the umbrella for every way of naming what colors and styles mean, as in Vega-Lite.
581
+ - **Parameter names follow Vega-Lite where a standard term exists** (axis, legend, aggregation), keeping ForgeGraph's nesting, snake_case and units. A third of the parameters have no Vega-Lite equivalent, so its names weren't adopted wholesale. "Legend" is the umbrella for every way of naming what colors and styles mean, as in Vega-Lite.
583
582
  - **Every size is × figure.base_size,** so a theme draws correctly in any format.
584
583
  - **The command is `forgegraph`.** `fg` is a built-in command in bash and zsh, and a shell runs its built-in before any program of that name.
585
584
  - **A tab's name is what its tab shows.** File names and links use it lowercased, with spaces as hyphens, so there's no separate label to keep in step.
@@ -0,0 +1,14 @@
1
+ # Pack example: acme/slide, a format
2
+
3
+ This page mirrors examples/packs/acme/formats/slide.yaml, which is the specification. A test fails if they ever differ.
4
+
5
+ name: slide          *# \[text\]; The name of the format.*\
6
+ version: 0          *# \[int ≥ 0\]; The version number of the specification.*\
7
+ description: A full-width graph on a 16:9 slide.          *# \[text\]; The description of the format.*\
8
+ extends: forgegraph_default/compact_format          *# \[None, format\]; Each parameter not listed is inherited from this format. If None, this file must list every parameter.*\
9
+  \
10
+ parameters:          *# !var/!fix sets whether a GraphSpec or layout may change a property. Subcategories/parameters inherit the setting of the one above them unless explicitly overridden.*\
11
+     figure:\
12
+         width: 254          *# \[auto, float > 0\]; The figure's width in mm. auto calculates it from the height and plot_area.aspect_ratio. Width and height can't both be auto.*\
13
+         height: 143          *# \[auto, float > 0\]; The figure's height in mm. auto calculates it from the width and plot_area.aspect_ratio.*\
14
+         base_size: 20          *# \[float > 0\]; The base size in points. The sizes of text, lines, ticks and gaps are multiples of it.*
@@ -1,36 +1,31 @@
1
- # Pack example: acme/polarization-curve, a GraphSpec to extend
1
+ # Pack example: acme/polarization-curve, a GraphSpec
2
2
 
3
3
  This page mirrors examples/packs/acme/graphspecs/polarization-curve.yaml, which is the specification. A test fails if they ever differ.
4
4
 
5
5
  name: polarization-curve          *# \[text\]; The name of the GraphSpec.*\
6
- version: 0          *# \[int\]; The version number of the specification.*\
6
+ version: 0          *# \[int ≥ 0\]; The version number of the specification.*\
7
7
  description: Acme's polarization curve, cell voltage against current density, one line per cell.          *# \[text\]; The description of the GraphSpec.*\
8
8
  extends: None          *# \[None, graphspec\]; Each parameter not listed is inherited from this GraphSpec. If None, this file must list every parameter.*\
9
9
   \
10
- fixed_parameters: None          *# List of fixed parameters for the GraphSpec, None if none.*\
11
-  \
12
- variable_parameters:          *# List of variable parameters for the GraphSpec, None if none.*\
10
+ parameters: !var          *# !var/!fix sets whether a GraphSpec may change a property. Subcategories/parameters inherit the setting of the one above them unless explicitly overridden.*\
13
11
      specifications:\
14
12
          graph_type: xy          *# \[graph type\]; The graph type.*\
15
13
          theme: acme/acme          *# \[theme\]; The theme.*\
16
14
          format: acme/slide          *# \[format\]; The format.*\
17
15
      text:\
18
-         title: None          *# \[None, text\]; Graph title. None: none.*\
16
+         title: None          *# \[None, text\]; The graph's title. If None, there is no title.*\
19
17
      data:\
20
-         file: None          *# \[None, text\]; Parquet file holding the DataFrame to draw. None: passed through the Python API.*\
21
-         preprocessing: None          *# \[None, text\]; Where the code that made the data is. None: not given.*\
18
+         file: None          *# \[None, text\]; The Parquet file holding the DataFrame to draw. If None, the DataFrame is passed through the Python API.*\
19
+         preprocessing: None          *# \[None, text\]; Where the code that made the data is. If None, it's not given.*\
22
20
      axis:\
23
21
          x:\
24
22
              data:\
25
-                 column: Current density (mA/cm²)          *# \[column\]; Horizontal axis column.*\
26
-             title: "Current density (mA/cm^2)"          *# \[None, column_name, text\]; Axis title.*\
23
+                 column: Current density (mA/cm²)          *# \[None, column\]; The column on this axis when it's arrangement.base_axis.*\
24
+             title: "Current density (mA/cm^2)"          *# \[None, column_name, text\]; The axis title.*\
27
25
          y:\
28
-             data:\
29
-                 column: Cell voltage (V)          *# \[columns\]; Vertical axis column.*\
30
-             title: column_name          *# \[None, column_name, text\]; Axis title.*\
31
-     encoding:\
32
-         color:\
33
-             column: Cell          *# \[None, column_name, column\]; A color per value of this column.*\
34
-     mark:\
35
-         marker:\
36
-             show: true          *# \[true, false\]; Draw markers.*
26
+             title: column_name          *# \[None, column_name, text\]; The axis title.*\
27
+     series:\
28
+         Cell voltage (V):\
29
+             color: Cell          *# \[series, hex color, column\]; The series' color.*\
30
+             marker:\
31
+                 show: true          *# \[true, false\]; Whether to draw markers.*