You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
Added
Day-of-week scheduling: <subsection> spec layer + clm schedule
(issue #261). A <section>'s <topics> may now group <topic>s into
optional <subsection weekday="mon">…</subsection> elements (<section> =
week, <subsection> = day) with an optional <name> label override and enabled="false". The layer is purely additive: clm build flattens
subsections away, so a spec with subsections builds byte-identically to
the same spec with the wrappers removed — no output-dir or topic-resolution
changes. A new top-level clm schedule command exports the certification
day-listing from the resolved course in Markdown (one table per week:
weekday / video / topic) or CSV (one row per deck), single-language via --lang (default de). clm outline renders subsections indented under
their section (with --include-disabled surfacing disabled ones), and clm validate adds four advisory subsection checks (duplicate weekday,
out-of-order weekdays, empty day, and bare-topics-mixed-with-subsections).
See clm info spec-files and clm info commands.
clm slides assign-ids --accept-code-derived — a deterministic, opt-in
fallback that mints a slide_id for bare-expression code subslides the AST
extractors can't name ((1 + 1j) * (1 + 1j) → 1-1j-1-1j, letters[0:3] → letters-0-3), by slugifying the cell's first real code line. Previously
these hard-refused, with the non-deterministic LLM (--llm-suggest) as the
only non-manual escape — so the bilingual→split conversion needed a human to
hand-author ids on both split halves. The first-code-line scanner is
comment-token-aware, so it also completes non-Python decks
(.cs/.cpp/.java/.ts), which ast can never parse. It is independent
of --accept-content-derived (separately gated, so the content-derived
minting funnels never start emitting opaque code-line slugs); the conversion
pipeline passes both. Genuinely empty / pure-punctuation / magic-only cells
still refuse. (#251)
clm slides normalize gains a preamble_code operation (default-on) that
fixes issue #253: executable code between the # {{ header(…) }} macro call and
the first # %% cell has no cell marker, so jupytext folds it into the header
cell — and, at build time, into the title markdown. In the bilingual header(de, en) macro that code rides the EN title (silently dropped from a DE
build); in a split .de.py half it rides the DE title (kept), so the bilingual
and split builds diverge on the DE side and the conversion is not
render-neutral. The new op moves the code into its own shared # %% code cell —
included identically in every build and copied verbatim to both split halves —
so the builds become byte-identical and the code is finally executed as code
rather than rendered as markdown text. It runs first among the normalize passes,
is idempotent, and is a no-op on a conforming deck.
Jinja {% include %} in slide source now resolves against the topic's own
siblings (PR #258). A deck that does {% include "add.h" %} to show a file
sitting next to it previously failed with TemplateNotFound, because the Jinja
environment only loaded the bundled templates_<prog_lang>/ package. The loader
is now a ChoiceLoader: the bundled package is tried first (so macros.j2
and friends can never be shadowed by a same-named sibling), then the notebook's
topic siblings as a fallback — a FileSystemLoader on source_dir in Docker
source-mount mode and/or a DictLoader decoded from payload.other_files in
direct mode (non-UTF-8 siblings are skipped). When a deck has no siblings the
plain package loader is used unchanged, so existing courses are unaffected. See
the new "Jinja {% include %} in slide source" section under clm build in clm info commands.
Changed
clm validate (format group) and clm slides split now warn about
preamble code (issue #253). validate emits a warning-severity finding and split prints a non-fatal warning: to stderr (it never rewrites the source,
so the byte-identical round-trip is preserved). Both point at clm slides normalize for the fix. Top-of-file code before the # j2 import line (a true
file preamble, e.g. a leading import os) is already render-neutral and is not
flagged.
CI/release workflows upgraded to Node.js 24-based actions. GitHub is
forcing JavaScript actions onto Node.js 24 (Node.js 20 is deprecated and
removed from runners in September 2026). actions/checkout, setup-python, setup-java, astral-sh/setup-uv, codecov/codecov-action, and docker/setup-buildx-action are bumped to their current Node-24 majors. No
behavioral change to CI or the release pipeline. A grouped monthly Dependabot
config (.github/dependabot.yml) now keeps the actions current so the next
runtime deprecation arrives as a routine PR.
bump-my-version no longer creates a local vX.Y.Z tag (tag = false in [tool.bumpversion]). The Release workflow already creates the authoritative
tag on the server after the CI-green gate; dropping the local tag removes a
must-never-push footgun and the post-release tag-reconciliation step. Release
procedure docs updated accordingly.
Fixed
Notebook-worker output is now written with LF newlines on every platform
(PR #260). All notebook-worker outputs (executed .ipynb, jupytext .py, and
the HTML body) go through one open(..., "w") write that, lacking a newline=
argument, let Python's universal-newline translation rewrite every \n to os.linesep — CRLF on Windows Direct workers, LF on Docker/Linux. So a Windows clm build produced CRLF working-tree files, yielding trailing-^M diffs and
"CRLF will be replaced by LF" warnings in course repos that normalize to eol=lf. The write now pins newline="\n", matching the convention used
elsewhere in the codebase, so output is byte-identical regardless of host OS.
Pool-shutdown orphan jobs are no longer mis-blamed on the user (PR #259).
A worker-pool shutdown race could leave a valid job (e.g. a drawio diagram)
stamped with the orphan sentinel; the drawio categorizer matched none of its
specific patterns and fell through to error_type="user" ("Check your DrawIO
diagram for errors"). Because user errors are persisted to processing_issues
(and the content hash never changes), the stale error was then replayed on
every subsequent cached build. categorize_job_error now checks for the
orphan sentinel before the per-job-type dispatch and returns an infrastructure / orphaned_job error with re-run guidance — infrastructure
errors are not cached, so the transient failure stays out of the replay store
and the false diagram-blame disappears. The central placement also covers
notebook and plantuml orphans from the same race.