Skip to content

CLM v1.9.1

Choose a tag to compare

@github-actions github-actions released this 07 Jun 05:08
· 263 commits to master since this release
2c0d45d

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.