 \documentclass[
load-preamble+
]{osgdoc}
\usepackage{iftex}
\ifLuaTeX\else
  \errmessage{LuaLaTeX is required to run this document.}
\fi
% Get ollm verison from Version.pm
\begin{luacode*}
  tex.print("\\gdef\\ollmversion{??}")
  local file = io.open("Version.pm","r")
  if file then
    for line in file:lines() do
      if line:match("VERSION = ") then
          tex.print("\\gdef\\ollmversion{"..line:match("'([^']*)'").."}")
        break
      end
    end  
  end
\end{luacode*}

\usepackage{enumitem}
\usepackage{fontspec}
\usepackage{microtype}
\usepackage{xspace}
\usepackage[
languages={de,en},
map={de=german,en=english},
targetlang={job=2},
load babel
]{langselect}

\newpackagename{\ollm}{OLLM}
\newpackagename{\osglecture}{osglecture}

\makeindex
\setcnltx{
  version  = \ollmversion,
  date     = \today,
  title    = {\ollm~-- OSG LaTeX Lecture Maker},
  info     = {\ldeen{Nutzerhandbuch}{User Manual}},
  package = {osglecture},
  authors  = {Matthias Werner[matthias.werner@informatik.tu-chemnitz.de]},
  abstract={
  \ldeen{
      \ollm{} ist ein in Perl geschriebenes Buildscript für die 
      \osglecture-Klasse.
      Ursprünglich aus einem rc-Script für \pkg{latexmk} entstanden, baut
      es mit \osglecture{} erstellte Vorlesungsdokumente, wählt
      Dokumenttyp und Sprache, startet die eigentliche Übersetzung 
      über \pkg{latexmk}, verwaltet kapitelübergreifende Referenzen und
      kopiert fertige PDFs auf konfigurierte Deploymentziele.
    }{
      \ollm is a build script written in Perl for the \osglecture{} class.
      Originally derived from an rc script for \pkg{latexmk},
      it builds lecture documents created with \osglecture, selects
      the document type and language, starts the actual compilation 
      via \pkg{latexmk}, manages cross-chapter references, and
      copies finished PDFs to configured deployment destinations.    
    }\par\medskip
    \ldeen{
      Dieses Handbuch beschreibt die Bedienung von \ollm{} 
      sowie die Formate der genutzten Konfigurationsdateien.
    }{
      This manual describes how to use \ollm{} 
      as well as the formats of the configuration files used.
    }
  },
  url      =https://github.com/werner-matthias/osglecture,
  build-title
}
%\AtBeginEnvironment{verbatim}{\linespread{1.0}\selectfont}
%\emergencystretch=2em

%\hypersetup{
%  pdftitle  = {OLLM -- Benutzerhandbuch},
%  pdfauthor = {OSG},
%  colorlinks,
%  linkcolor = blue!50!black,
%  urlcolor  = blue!50!black
%}

\begin{document}

\section{TL;DR}
\begin{itemize}
  \item \ldeen{Wenn Sie in einem bestehenden 
    \osglecture-Kurs\footnote{D.\,h., mit korrekter Verzeichnisstruktur und korrektem Manifest}
    die Vorlesungsfolien einer Lehreinheit in deutscher Sprache neu generieren lassen wollen, 
    wechseln sie zum Verzeichnis dieser Einheit und geben ein:}{}
  \begin{verbatim}
    > ollm slides --language=de
  \end{verbatim}

  \item \ldeen{Sie wollen alle Scriptkapitel der Erstsprache der Lehrveranstaltung
   zu einem Script zusammenstellen. Dazu muss das Arbeitsverzeichnis irgendein
   Projektverzeichnis sein und Sie geben ein:}{}
  \begin{verbatim}
    > ollm script --scope=collection
  \end{verbatim}

  \item \ldeen{Sie wollen alle Handouts der Erstsprache übersetzen. 
  Dazu muss das Arbeitsverzeichnis irgendein
   Projektverzeichnis sein und Sie geben ein:}{}
  \begin{verbatim}
    > ollm handout --scope=series
  \end{verbatim}

  \item \ldeen{Sie wollen alle englischen Handouts an ihren Zielort kopieren. 
  Dazu muss das Arbeitsverzeichnis irgendein
   Projektverzeichnis sein und Sie geben ein:}{}
  \begin{verbatim}
    > ollm deploy handout lang=en --scope=series
  \end{verbatim}

\end{itemize}
  
\section{Schnelleinstieg}
\subsection{Installation}
\ldeen{Die Installation erfolgt über mit der Installation des \osglecture-Pakets über \code{l3build}:
}{

}
\begin{verbatim}
  > l3build install
\end{verbatim}
\ldeen{
Dabei landet das Skript in \code{TEXMFHOME/scripts/osglecture/}, das nicht im
Suchpfad der Shell liegt. Unter Linux und macOS genügt ein einmaliger Link in
ein Verzeichnis des Suchpfads:
}{
The script ends up in \code{TEXMFHOME/scripts/osglecture/}, which is not on the
shell's search path. On Linux and macOS, link it once into a directory that is:
}
\begin{verbatim}
  > ln -s "$(kpsewhich -var-value TEXMFHOME)/scripts/osglecture/ollm" ~/.local/bin/ollm
\end{verbatim}
\ldeen{
Unter Windows nehmen Sie das Verzeichnis in \code{PATH} auf; es enthält den
Starter \code{ollm.cmd}.
}{
On Windows, add that directory to \code{PATH}; it contains the launcher
\code{ollm.cmd}.
}

\ldeen{
\ollm{} benötigt für seine Ausführung Perl~5.30 oder neuer, \code{latexmk} und Lua\LaTeX. Eine
vollständige Installation prüfen Sie mit:
}{}
\begin{verbatim}
>  ollm doctor
\end{verbatim}


Wechseln Sie zum Anlegen eines Projekts in dessen künftige Wurzel und führen
Sie aus:

\begin{verbatim}
ollm newproject --profiles=taggable
\end{verbatim}

Der Befehl erzeugt \texttt{ollmconfig.toml}. Die Option \texttt{--profiles}
ist Pflicht; ohne sie erklärt \texttt{newproject} beide Varianten und schreibt
nichts:

\begin{description}
\item[\texttt{taggable}] \texttt{slides}, \texttt{handout} und
  \texttt{screen} nutzen \texttt{ltx-talk}, \texttt{script} nutzt
  \texttt{book}. Alle Targets laufen mit \verb|\DocumentMetadata| und
  eingeschaltetem Tagging; \texttt{Include/documentmetadata.tex} wird
  angelegt. Das setzt ein aktuelles \TeX\ Live voraus, und Beamer-Themes
  sowie beamerspezifischer Code funktionieren nicht.
\item[\texttt{classic}] \texttt{slides}, \texttt{handout} und
  \texttt{screen} nutzen \texttt{beamer}, \texttt{script} nutzt
  \texttt{scrbook}. Es entsteht kein Tagged PDF; davon abhängige Funktionen
  wie das Handout-Layout \texttt{ruled} und die Presenter-Uhr fehlen.
\end{description}

Die Wahl steht danach in \texttt{[targets.defaults]}; einzelne Targets lassen
sich dort mit \texttt{profile = "..."} von Hand mischen. Existiert bereits
eine alte \texttt{ollmconfig.pl}, wird diese soweit möglich konvertiert. Ein
vorhandenes TOML-Manifest wird niemals überschrieben.

Eine kleine Serie kann so aufgebaut sein:

\begin{verbatim}
my-course/
  ollmconfig.toml
  Include/
    projectconfig.tex
    documentmetadata.tex
  010-introduction/
    main.tex
  020-processes/
    main.tex
\end{verbatim}

In einem Unit-Verzeichnis bauen Sie beispielsweise ein deutsches Skript:

\begin{verbatim}
cd 010-introduction
ollm build script --language=de
\end{verbatim}

Die Kurzform \texttt{ollm script --language=de} ist gleichwertig. Die Targets
\texttt{slides}, \texttt{handout}, \texttt{screen} und \texttt{script} sind
mitgeliefert. \texttt{screen} erzeugt die Vortragendenfassung der Folien: Jede
Seite ist doppelt so breit und trägt rechts die Presenter-Seite der Klasse
\texttt{osglecture}; das Target setzt ein Profil voraus, das den Dokumenttyp
\texttt{screen} unterstützt (mitgeliefert: \texttt{beamer} und
\texttt{ltx-talk}). \texttt{ollm newproject} konfiguriert es wie
\texttt{slides} und \texttt{handout} direkt mit.

\section{Projektaufbau}

\subsection{Projektwurzel und Units}

Die Datei \texttt{ollmconfig.toml} markiert die Projektwurzel. OLLM sucht vom
Arbeitsverzeichnis aus aufwärts nach dieser Datei. Ein Unit-Verzeichnis trägt
einen dreistelligen physischen Ordnungspräfix:

\begin{verbatim}
010-introduction
020-processes
\end{verbatim}

Nach den drei Ziffern kann ein Target-Scope stehen. Die mitgelieferten
Definitionen verwenden unter anderem \texttt{a}/\texttt{as} für Langformen
und \texttt{b}/\texttt{bs}/\texttt{bh} für Präsentation, Folien und Handout.
Eine optionale Rolle folgt nach einem weiteren Bindestrich:

\begin{verbatim}
030-e-scheduling-details   Excursus
090-a-appendix             Appendix
099-i-collection           Integration
\end{verbatim}

Ohne Rollenpräfix ist die Unit regulärer Inhalt. Ein Excursus gehört zur
vorhergehenden sichtbaren Content-Unit. Seine logische Ordnungskennung lautet
beispielsweise \texttt{2e1}; weitere Exkurse erhalten \texttt{2e2} usw.
Anhänge führen den regulären Zähler weiter und ergänzen eine lokale Kennung,
beispielsweise \texttt{3ap1}, \texttt{4ap2}. Eine Integrationsunit besitzt
keine Ordnungsnummer.

Die logische Unit-ID stammt nicht aus dem Verzeichnisnamen. Eine reguläre Unit
deklariert sie ausdrücklich in LaTeX, beispielsweise:

\begin{verbatim}
\lecture[Prozesse]{Prozesse und Scheduling}{processes}
\end{verbatim}

Nach einem ersten erfolgreichen Build kennt OLLM die Zuordnung von
\texttt{processes} zum physischen Verzeichnis. Bis dahin ergeben unbekannte
kapitelübergreifende Referenzen wie bei LaTeX üblich einen Platzhalter und
eine Warnung.

\subsection{Gemeinsame TeX-Konfiguration}

Standardmäßig liegt projektweit geteiltes TeX-Material in \texttt{Include}.
\texttt{Include/projectconfig.tex} enthält fachliche und darstellerische
TeX-Konfiguration, beispielsweise Titel, Autor und Sprachabbildung:

\begin{verbatim}
\title{Betriebssysteme}
\author<presentation>[M. Mustermann]{Max Mustermann}
\author<longform>[Mustermann]{Max Mustermann}

\usepackage[
  languages={de,en},
  targetlang={\OsgLectureLanguage},
  map={de=ngerman,en=british}
]{langselect}
\end{verbatim}

Titel und Sprachabbildung gehören bewusst nicht in TOML. Dadurch kann dieselbe
TeX-Konfiguration auch von einem Standalone-Dokument verwendet werden.

Benötigt ein Target frühe PDF-Metadaten, enthält
\texttt{Include/documentmetadata.tex} den Aufruf von
\verb|\DocumentMetadata|. Ein einfaches Beispiel ist:

\begin{verbatim}
\DocumentMetadata{lang=\OsgLectureRequestedLanguage}
\end{verbatim}

Ob diese Datei erforderlich oder deaktiviert ist, bestimmt das jeweilige
Target beziehungsweise ein Override im Projektmanifest.

\section{Aufruf und Optionsmodell}

Die allgemeine Form lautet:

\begin{verbatim}
ollm [globale Optionen] [Aktion] [Target] [Aktionsoptionen]
\end{verbatim}

Ohne Aktion wird \texttt{build} angenommen. Mitgelieferte Targetnamen können
als nacktes Wort oder mit historischem Pluspräfix geschrieben werden:

\begin{verbatim}
ollm slides
ollm +slides
ollm build --target=slides
\end{verbatim}

Aliase sind \texttt{beamer} und \texttt{presentation} für \texttt{slides}
sowie \texttt{article} für \texttt{script}. Projektdefinierte Targets werden
mit \texttt{--target=NAME} gewählt.

\subsection{Allgemeine Optionen}

\begin{description}
\item[\texttt{--help}] zeigt die eingebaute Kurzhilfe.
\item[\texttt{--version}] zeigt die OLLM-Version.
\item[\texttt{--format=text|json}] wählt menschen- oder maschinenlesbare
  Ausgabe. JSON eignet sich für CI und Werkzeuge.
\item[\texttt{--color=auto|always|never}] steuert farbige Terminalausgabe.
\item[\texttt{--config=DATEI}] verwendet ein bestimmtes Projektmanifest.
\item[\texttt{--project-root=VERZEICHNIS}] verwendet die angegebene
  Projektwurzel.
\item[\texttt{--non-interactive}] unterdrückt interaktive Rückfragen.
\item[\texttt{--overwrite}] erlaubt bei \texttt{deploy} das Ersetzen einer
  abweichenden Zieldatei, wenn die Projektpolicy eine explizite Freigabe
  verlangt.
\item[\texttt{--permissions=all|KEY=WERT,...}] ersetzt bei \texttt{deploy}
  die konfigurierten PDF-Rechte aller Dokumenttypen, etwa
  \texttt{--permissions=print=none}; \texttt{all} deployt ohne
  Einschränkungen.
\item[\texttt{--owner-password=KENNWORT}] legt bei \texttt{deploy} das
  Kennwort fest, mit dem sich die PDF-Einschränkungen aufheben lassen; ohne
  Angabe ist es zufällig und wird nicht gespeichert.
\item[\texttt{+enforce+}, \texttt{--enforce+}] verlangt das Pluspräfix bei
  Aktions- und eingebauten Targetwörtern. Das verhindert, dass ein Dateiname
  wie \texttt{slides} als Target interpretiert wird. \texttt{+force+} ist ein
  veralteter Alias.
\end{description}

Unbekannte kompatible Minusoptionen werden bei einem Build an \texttt{latexmk}
weitergereicht. Zusätzliche RC-Dateien und Optionen, die den kontrollierten
Buildvertrag umgehen würden, lehnt OLLM ab.

\section{Bauen}

\subsection{Einzelner Build}

\begin{verbatim}
ollm build script --language=en
ollm build --target=slides --language=de --source=main.tex
\end{verbatim}

\texttt{--language} wählt eine für das Target konfigurierte Sprache. Ohne
Angabe gilt die Projektstandardsprache, sofern das Target sie unterstützt,
ansonsten die erste Sprache des Targets. \texttt{--source} ist standardmäßig
\texttt{main.tex}.

\texttt{--scope} bestimmt, auf welche Units und Dokumentinstanzen der Build wirkt:

\begin{description}
\item[\texttt{current}] ist der Default und baut genau das ausdrücklich
  gewählte beziehungsweise das Default-Target in der gewählten oder
  Defaultsprache der aktuellen Unit.
\item[\texttt{unit}] baut alle für die aktuelle Unit zulässigen Targets und
  Sprachen. Ein ausdrücklich angegebenes Target oder eine ausdrücklich
  angegebene Sprache schränkt diese Matrix ein.
\item[\texttt{series}] baut das gewählte beziehungsweise das Default-Target in
  der gewählten oder Defaultsprache für alle Units, auf die das Target
  anwendbar ist. Integrationsunits gehören nicht zu dieser Auswahl.
\item[\texttt{collection}] sucht für den Dokumenttyp des Targets die eindeutig
  passende Integrationsunit und baut dort. Der Scope funktioniert unabhängig
  vom Arbeitsverzeichnis. Keine oder mehrere passende Integrationsunits sind
  ein Fehler.
\end{description}

\texttt{current} und \texttt{unit} verlangen ein Unit-Verzeichnis als
Ausgangspunkt; \texttt{series} und \texttt{collection} können auch aus der
Projektwurzel verwendet werden.

\texttt{--rebuild} verlangt einen vollständigen \texttt{latexmk}-Neubau.
\texttt{--dry-run} startet kein LaTeX, sondern zeigt die normalisierte Auswahl:

\begin{verbatim}
ollm build script --language=en --dry-run
ollm build script --language=en --dry-run --format=json
\end{verbatim}

\verb|--debug=tex|, \texttt{ollm} oder \texttt{tex+ollm} aktiviert
Diagnoseausgaben für die gewählte Ebene. \verb|--warnings=all|,
\texttt{important} oder \texttt{none} wählt die gewünschte Warnungsmenge.

\subsection{Buildmatrix mit \texttt{--all}}

\texttt{--all} erweitert die Target-/Sprachauswahl des gewählten Build-Scopes
auf alle im Manifest vorgesehenen Kombinationen. Target-Scope und Rolle jeder
Unit filtern die Matrix. Ohne ausdrücklichen Scope bleibt \texttt{current}
wirksam; der Befehl baut dann nicht automatisch jede Unit der Serie.

\begin{verbatim}
ollm build --all
\end{verbatim}

\subsection{Abhängigkeiten mit \texttt{--resolve}}

\texttt{--resolve} verfolgt die von LaTeX gemeldeten serieninternen
Abhängigkeiten und baut veraltete Produzenten beziehungsweise Konsumenten bis
zu einem Fixpunkt:

\begin{verbatim}
ollm build script --resolve
\end{verbatim}

Zyklen sind erlaubt. Noch nie direkt gebaute logische Units kann OLLM nicht
aus Verzeichnisnamen erraten; bauen Sie eine solche Unit zunächst einmal in
der benötigten Kombination aus Dokumenttyp und Sprache. Die Höchstzahl der Fixpunktrunden wird in
\verb|[build.resolve]| konfiguriert.

\subsection{Standalone-Modus}

Für ein Dokument ohne Serienmanifest bleibt die kompatible Aktion
\texttt{standalone} verfügbar:

\begin{verbatim}
ollm standalone main.tex
\end{verbatim}

Dokumenttyp und Sprache stammen dabei aus den Klassenoptionen des Dokuments;
\texttt{--target}, \texttt{--language} und \texttt{--all} sind nicht
zulässig.

\section{Report, Check und Doctor}

\subsection{Report}

\texttt{report} beschreibt Units, vorhandene Dokumentinstanzen,
Ausgabedateien, den jeweils gültigen Buildstand, Kapitel- und Ordnungsnummern, Konfigurationssignaturen und
Abhängigkeiten. Befunde ändern seinen Exitstatus nicht, solange der Zustand
lesbar ist.

\begin{verbatim}
ollm report
ollm report --scope=unit
ollm report --format=json
\end{verbatim}

In der Projektwurzel ist der Default-Scope \texttt{series}; in einer Unit
\texttt{current}. \texttt{unit} muss ausdrücklich gewählt werden. Eine
entdeckte, aber unbenutzte und ungebaute Unit erscheint als \texttt{dormant}
und ist kein Fehler.

\subsection{Check}

\texttt{check} liest dieselben Daten, bewertet Inkonsistenzen jedoch als
Fehler. Es baut nichts. Geprüft werden unter anderem Artefakte, Identitäten,
Rollen und Scopes, Target-/Sprachzulässigkeit, Job-ID, Ordnungsnummer und
Referenzabhängigkeiten.

\begin{verbatim}
ollm check
ollm check --scope=current --target=script --language=de
ollm check --format=json
\end{verbatim}

Ein inkonsistenter erforderlicher Zustand ergibt Exitcode~3. Ruhende Units
erzeugen keine Buildpflicht. Die aggregierte Konfiguration kann sich bereits
durch eine neue ruhende Unit ändern; ein solcher unspezifischer Unterschied
wird daher als Warnung gemeldet, konkrete Vertragsabweichungen bleiben Fehler.

\subsection{Doctor}

\texttt{doctor} prüft global Perl, \texttt{latexmk}, LuaLaTeX,
\texttt{kpsewhich}, den TOML-Parser sowie erforderliche und optionale
TeX-Pakete. Innerhalb eines Projekts prüft es zusätzlich Manifest,
Definitionen, Projektdateien, Schreibbarkeit, gespeicherten Zustand und
Shell-Escape-Policy.

\begin{verbatim}
ollm doctor
ollm doctor --project-root=/path/to/project
ollm doctor --format=json
\end{verbatim}

Fehlende notwendige Werkzeuge ergeben Exitcode~69. Optionale Pakete werden
angezeigt, führen allein aber nicht zum Fehlschlag.

\section{Aufräumen}

\subsection{Clean}

\texttt{clean} entfernt ausgewählte Arbeits- oder Zustandsdaten. Der
Default-Level ist \texttt{aux}, der Default-Scope \texttt{current}.

\begin{description}
\item[\texttt{--level=aux}] entfernt Hilfsdateien, behält aber das lokale PDF.
\item[\texttt{--level=build}] entfernt das isolierte Buildverzeichnis.
\item[\texttt{--level=state}] entfernt den gespeicherten Projektzustand.
\item[\texttt{--level=all}] kombiniert \texttt{build} und \texttt{state}.
\end{description}

Scopes sind \texttt{current}, \texttt{unit} und \texttt{series}. Ein
Serien-Clean aus einer Unit verlangt eine Rückfrage; von der Projektwurzel aus
nicht. Mit \texttt{--dry-run} wird nur der Löschplan gezeigt.

\begin{verbatim}
ollm clean --level=aux
ollm clean --level=build --scope=unit --dry-run
ollm clean --level=state --scope=series
\end{verbatim}

\subsection{Prune}

\texttt{prune} entfernt aufgegebene temporäre Zustände und ersetzte ältere
Buildstände. Zustände von inzwischen fehlenden physischen Units werden nur
mit \texttt{--stale-units} entfernt:

\begin{verbatim}
ollm prune --dry-run
ollm prune --stale-units
\end{verbatim}

Wenn kein OLLM- oder \texttt{latexmk}-Prozess läuft, darf das gesamte
Verzeichnis \texttt{.osglecture} als radikale Reparatur gelöscht werden. Dabei
gehen auch alle bekannten Zuordnungen logischer Unit-IDs verloren; betroffene
Units müssen anschließend wieder direkt gebaut werden.

\section{Deployment}

\texttt{deploy} kopiert bereits erfolgreich gebaute PDFs. Es startet
keinen Build und legt keine Zielverzeichnisse an.

\begin{verbatim}
ollm deploy handout
ollm deploy --target=script --scope=series
ollm deploy --target=script --scope=collection
ollm deploy --all --overwrite
\end{verbatim}

In der Projektwurzel gilt standardmäßig \texttt{series}, in einer Unit
\texttt{current}. \texttt{unit} wählt alle vorhandenen Dokumentinstanzen der Unit;
\texttt{collection} wählt die Integrationsdatei des Dokumenttyps unabhängig
vom Arbeitsverzeichnis. Bei Serien-Deployment entscheidet
\verb|deployment.series|, ob Einzelunits, Collection oder beide kopiert
werden.

Fehlt ein Zielpfad, meldet OLLM einen Fehler und überspringt weitere Versuche
mit demselben Pfad. Andere konfigurierte Ziele werden weiter bearbeitet. Eine
vorhandene identische Datei bleibt unverändert. Ob eine abweichende Datei
\texttt{--overwrite} verlangt, bestimmt \verb|security.deployment.overwrite|.

Deployte Kopien können eingeschränkte PDF-Rechte tragen, etwa keine
Druckerlaubnis. \verb|[deployment.permissions]| ist der Default aller
Dokumenttypen, \verb|[deployment.types.DOCTYPE.permissions]| ersetzt ihn
vollständig; nicht genannte Rechte bleiben erlaubt:

\begin{verbatim}
[deployment.permissions]
print = "none"     # full | low | none
extract = false    # Text und Grafik kopieren
modify = false     # Seiteninhalt ändern
annotate = false   # kommentieren
form = false       # Formulare ausfüllen und signieren
assemble = false   # Seiten einfügen, löschen, drehen
\end{verbatim}

Eingeschränkte Kopien erzeugt OLLM mit dem externen Programm \texttt{qpdf}
(AES-256, ohne Öffnungskennwort, mit zufälligem oder per
\texttt{--owner-password} angegebenem Eigentümerkennwort). Fehlt
\texttt{qpdf}, fragt \texttt{deploy} im Terminal, ob ohne Einschränkungen
kopiert werden soll; mit \texttt{--non-interactive} oder ohne Terminal bricht
es mit Exitcode~69 ab. Die Einschränkungen sind leicht zu entfernen und daher ein
Hinweis an die Lesenden, keine Zugriffskontrolle.

\section{Das Projektmanifest \texttt{ollmconfig.toml}}

\subsection{Vollständiges Beispiel}

\begin{verbatim}
schema = 1
bundle_preset = "OSG lecture/1"

[project]
id = "bs"

[project.tex]
directory = "Include"
config = "projectconfig.tex"

[languages]
available = ["de", "en"]
default = "de"

[targets.slides]
languages = ["de", "en"]
document_metadata = "disabled"

[targets.handout]
languages = ["de"]

[targets.script]
languages = ["de", "en"]
document_metadata = "required"

[security]
shell_escape = "restricted"

[build.resolve]
max_rounds = 8

[deployment]
series = "both"

[deployment.roles]
content = ""
appendix = "A"
excursus = "E"
integration = ""

[deployment.types.handout]
paths = ["deployment/handouts", "/srv/lecture/handouts"]
filename = "{role}{chapter:02}-{unit}-{lang}.pdf"
collection_filename = "{series}-{doctype}-{lang}.pdf"
series = "units"

[deployment.types.handout.units.introduction]
filename = "{chapter:02}-intro-{lang}.pdf"

[security.deployment]
overwrite = "explicit"
\end{verbatim}

Unbekannte Schlüssel sind Fehler. Alle Bezeichner und Pfade müssen die in den
folgenden Abschnitten beschriebenen Bedingungen erfüllen.

\subsection{Wurzelschlüssel}

\begin{description}
\item[\texttt{schema}] Pflichtwert \texttt{1}. Er bezeichnet das
  Dateischema, nicht die OLLM- oder Bundleversion.
\item[\texttt{bundle\_preset}] Referenz auf ein Bundle-Preset, gewöhnlich
  \verb|"OSG lecture/1"|. Fehlt der Schlüssel, verwendet OLLM den
  Nutzerdefault beziehungsweise den eingebauten Default.
\end{description}

Weitere erlaubte Tabellen sind \texttt{project}, \texttt{languages},
\texttt{targets}, \texttt{security}, \texttt{build} und optional
\texttt{deployment}.

\subsection{\texttt{[project]} und \texttt{[project.tex]}}

\verb|project.id| ist die technische, portable Serien-ID. Sie geht in
Jobnamen und Dokumentidentitäten ein und sollte nach dem ersten Build nicht
leichtfertig geändert werden.

\verb|project.tex.directory| ist ein projektwurzelrelatives Verzeichnis;
Default ist \texttt{Include}. Absolute Pfade und Pfade oberhalb der
Projektwurzel sind nicht zulässig. \verb|project.tex.config| ist ein reiner
Dateiname ohne Verzeichniskomponenten; Default ist
\texttt{projectconfig.tex}. Die Datei darf neben Einstellungen auch
projektlokale Pakete aus dem gemeinsamen Verzeichnis verwenden.

\subsection{\texttt{[languages]}}

\verb|languages.available| ist eine nichtleere Liste portabler kurzer
Sprachkennungen. Doppelte Werte und Kollisionen, die sich nur in Groß- und
Kleinschreibung unterscheiden, sind unzulässig. \verb|languages.default|
muss in dieser Liste stehen.

Diese Tabelle beschreibt ausschließlich die Buildmatrix. Die Zuordnung zu
\texttt{babel}- oder anderen LaTeX-Sprachnamen gehört in
\texttt{projectconfig.tex}.

\subsection{\texttt{[targets.NAME]}}

Für jedes Projekt-Target ist eine Tabelle erforderlich. \verb|languages| ist
eine nichtleere Teilmenge von \verb|languages.available|. Optional überschreibt
\verb|document_metadata = "required"| oder \verb|"disabled"| den Default
der Targetdefinition.

Der Name muss durch eine eingebaute oder zusätzlich gefundene Targetdefinition
registriert sein. Schema~1 verlangt, dass Targetname und Doctype identisch
sind.

\subsection{\texttt{[security]}}

\verb|shell_escape| kann folgende Werte annehmen:

\begin{description}
\item[\texttt{off}] startet LuaLaTeX mit deaktiviertem Shell-Escape.
\item[\texttt{restricted}] verwendet die eingeschränkte Allowlist der
  TeX-Distribution; dies ist der Default.
\item[\texttt{full}] erlaubt vollständiges Shell-Escape und sollte nur für
  vertrauenswürdige Quellen verwendet werden.
\end{description}

In \verb|[security.deployment]| bestimmt \verb|overwrite| das Überschreiben
abweichender Zieldateien:

\begin{description}
\item[\texttt{explicit}] verlangt bei Bedarf \texttt{--overwrite}; Default.
\item[\texttt{automatic}] ersetzt abweichende Dateien ohne Zusatzoption.
\end{description}

\subsection{\texttt{[build.resolve]}}

\verb|max_rounds| ist eine positive Ganzzahl und begrenzt den Fixpunktlauf
von \texttt{build --resolve}. Ohne Angabe gelten acht Runden.

\subsection{Deploymenttabellen}

\verb|deployment.series| legt die Serienauswahl global fest:
\verb|"units"|, \verb|"collection"| oder \verb|"both"|. Ohne Angabe
gilt \verb|"both"|. In \verb|deployment.roles| können die Rollen
\texttt{content}, \texttt{appendix}, \texttt{excursus} und
\texttt{integration} auf frei wählbare Dateinamensbestandteile abgebildet
werden.

Für jeden deploybaren Dokumenttyp ist
\verb|[deployment.types.DOCTYPE]| erforderlich:

\begin{description}
\item[\texttt{paths}] nichtleere Liste von Zielverzeichnissen. Relative Pfade
  beziehen sich auf die Projektwurzel. OLLM legt sie nicht an.
\item[\texttt{filename}] Schablone für einzelne Units.
\item[\texttt{collection\_filename}] optionale Schablone für die
  Integrationsdatei; ohne sie wird \texttt{filename} verwendet.
\item[\texttt{series}] optionaler Override von
  \verb|deployment.series| für diesen Dokumenttyp.
\end{description}

Ein Dateiname für eine einzelne logische Unit kann ausschließlich über eine
Unit-Tabelle überschrieben werden:

\begin{verbatim}
[deployment.types.DOCTYPE.units.UNIT-ID]
filename = "..."
\end{verbatim}

Schablonen kennen:

\begin{description}
\item[\texttt{\{series\}}] technische Projekt-ID;
\item[\texttt{\{unit\}}] logische Unit-ID;
\item[\texttt{\{slug\}}] beschreibender Teil des Unit-Verzeichnisnamens
  ohne Nummer, Scope-Code und Rollenkürzel, etwa \texttt{processes} für
  \texttt{030-processes} oder \texttt{010a-e-processes};
\item[\texttt{\{ordinal\}}] logische Ordnungskennung;
\item[\texttt{\{chapter\}}] von LaTeX gemeldete Kapitelrepräsentation;
\item[\texttt{\{doctype\}}] Dokumenttyp;
\item[\texttt{\{lang\}}] Sprache;
\item[\texttt{\{role\}}] Wert aus \verb|deployment.roles|.
\end{description}

Dezimale Werte können mit einer Mindestbreite versehen werden, etwa
\texttt{\{chapter:02\}}. Bei \texttt{ordinal} wird nur der numerische Präfix
aufgefüllt: \texttt{1e2} wird zu \texttt{01e2}, \texttt{3ap1} zu
\texttt{03ap1}. Die Kapitelrepräsentation kann im Dokument mit
\verb|\OsgLectureDeploymentChapter{...}| ausdrücklich gesetzt werden.

\section{Lokale Projektkonfiguration \texttt{.ollmconfig.local.toml}}

Diese optionale, üblicherweise nicht versionierte Datei erweitert ausschließlich
die Suchpfade für Definitionen:

\begin{verbatim}
schema = 1

[definitions]
paths = ["Definitions", "/opt/osglecture/definitions"]
\end{verbatim}

\verb|schema| muss \texttt{1} sein. \verb|definitions.paths| ist eine Liste;
relative Einträge beziehen sich auf die Projektwurzel. Alle Pfade müssen
existieren. Doppelte Definitionen desselben Namens sind ein Fehler. Die lokalen
Pfade werden vor den mitgelieferten Definitionen durchsucht, ersetzen aber
nicht stillschweigend eine gleichnamige Definition.

\section{Persönliche Konfiguration}

Die persönliche Konfiguration enthält gegenwärtig nur den bevorzugten
Bundle-Preset:

\begin{verbatim}
schema = 1
bundle_preset = "OSG lecture/1"
\end{verbatim}

OLLM sucht sie unter Unix zunächst in \verb|$XDG_CONFIG_HOME/ollm/config.toml|
und sonst in \verb|$HOME/.config/ollm/config.toml|, unter Windows in
\verb|%APPDATA%\ollm\config.toml|. Die Umgebungsvariable
\verb|OLLM_USER_CONFIG| kann eine andere Datei bestimmen. Ein expliziter Wert
im Projektmanifest hat Vorrang.

\section{Definitionsdateien}

Definitionsdateien sind TOML-Dateien in den mitgelieferten oder lokal
konfigurierten Suchpfaden. Normale Projektanwender müssen sie nur für eigene
Targets bearbeiten.

\subsection{Bundle-Preset}

\begin{verbatim}
schema = 1
kind = "bundle-preset"
name = "OSG lecture"
version = "1"
\end{verbatim}

Erlaubt sind ausschließlich \verb|schema|, \verb|kind|, \verb|name| und
\verb|version|. Das Projekt referenziert das Preset als
\verb|"Name/Version"|, hier also \verb|"OSG lecture/1"|.

\subsection{Targetdefinition}

\begin{verbatim}
schema = 1
kind = "target"
name = "studyguide"
version = "1.0"
doctype = "studyguide"
profile_class = "longform"
document_metadata = "required"
unit_scopes = ["a", "as"]
\end{verbatim}

\begin{description}
\item[\texttt{name}] registrierter CLI-Targetname.
\item[\texttt{version}] Version dieser Definition, unabhängig vom Schema.
\item[\texttt{doctype}] Dokumenttyp; in Schema~1 identisch zu \texttt{name}.
\item[\texttt{profile\_class}] \verb|"presentation"| oder
  \verb|"longform"|. Die konkrete TeX-Profilwahl erfolgt in
  \texttt{projectconfig.tex}.
\item[\texttt{document\_metadata}] Default \verb|"required"| oder
  \verb|"disabled"|; das Projekt darf ihn überschreiben.
\item[\texttt{unit\_scopes}] optionale Liste ein- oder zweibuchstabiger
  Verzeichnisscopes, für die das Target gilt.
\end{description}

Legen Sie eine eigene Definition beispielsweise hier ab:

\begin{verbatim}
Definitions/targets/studyguide.toml
\end{verbatim}

Tragen Sie anschließend \texttt{Definitions} in die lokale Projektkonfiguration
ein, ergänzen Sie \verb|[targets.studyguide]| im Projektmanifest und wählen Sie
das Target mit \texttt{--target=studyguide}.

\section{Migration aus \texttt{ollmconfig.pl}}

\begin{verbatim}
ollm convertproject
ollm newproject --profiles=classic
\end{verbatim}

\texttt{convertproject} sucht eine alte \texttt{ollmconfig.pl}, erzeugt
eine konservative \texttt{ollmconfig.toml}, ohne den Perl-Code auszuführen,
und übernimmt erkennbare Metadaten aus \texttt{Include/lectdates.tex} nach
\texttt{Include/projectconfig.tex}. \texttt{newproject} erzeugt Manifest,
Include-Verzeichnis und eine Projektkonfiguration mit Dummy-Metadaten oder
verhält sich bei gefundenen Legacy-Dateien wie \texttt{convertproject}.
\texttt{convertproject} verwendet ohne \texttt{--profiles} den Profilsatz
\texttt{classic}, also die Klassen des alten Builds.
Bestehende Konfigurationsdateien werden nicht überschrieben.

Statisch erkennbare Sprachen, Shell-Escape-Einstellung, gemeinsames
TeX-Verzeichnis, einfache Deploymentzuweisungen und die von \texttt{osglecture}
unterstützten Metadatenbefehle aus \texttt{lectdates.tex} werden übernommen.
Dabei wird \texttt{\textbackslash ldeenr} zu \texttt{\textbackslash ldeen}
vereinheitlicht, ein \texttt{\textbackslash input} zu
\texttt{\textbackslash IncludeOsgLecturePreamble} (das Fragment läuft dann
\emph{nach} \texttt{\textbackslash LoadClass}), und die Sprachreihenfolge des
Manifests wird als \texttt{\textbackslash LectureProjectSetup} mit
\texttt{selectable}-Liste in \texttt{projectconfig.tex} gespiegelt.
Altbefehle ohne \texttt{osglecture}-Entsprechung
(\texttt{\textbackslash tucurl}, \texttt{\textbackslash logo}) werden
auskommentiert übernommen. Dynamische Perl-Ausdrücke, Passwörter,
Deploymentrestriktionen, gemeinsam genutzte Datenpfade und alte
Kapitelnummernlogik müssen anhand der ausgegebenen Warnungen manuell übertragen
werden. Eine automatisch erzeugte \texttt{projectconfig.tex} sollte früh
einmal testweise übersetzt werden.

Während der Migration dürfen beide Konfigurationsdateien nebeneinander liegen;
gebaut wird ausschließlich mit TOML. Liegt nur \texttt{ollmconfig.pl} vor,
bricht \ollm{} mit einem Hinweis auf \texttt{convertproject} ab.

\section{Exitcodes und CI}

\begin{description}
\item[0] erfolgreiche Aktion beziehungsweise lesbarer Report;
\item[1] Build-, Deployment- oder Zustandsfehler;
\item[2] ungültiger Aufruf oder ungültige Konfiguration;
\item[3] \texttt{check} hat eine Inkonsistenz gefunden;
\item[69] benötigtes Programm oder eine nicht verfügbare Ausführungsfunktion;
\item[128+Signal] signalbedingter Abbruch auf POSIX-Systemen, insbesondere
  130 bei Strg-C.
\end{description}

Für maschinelle Auswertung sollte \texttt{--format=json} verwendet werden.
\texttt{report} ist kein Freigabegate; für CI ist \texttt{check} vorgesehen.

\section{Weiterführende Dokumente}

Dieses Handbuch beschreibt ausschließlich die Benutzerschnittstelle. Hinweise
für Wartung und Weiterentwicklung stehen außerhalb der installierten
Nutzerdokumentation:

\begin{description}
\item[\texttt{development-docs/DESIGN.md}] OLLM-Architektur, Dateiverträge,
  Invarianten und Rationales;
\item[\texttt{development-docs/ARCHITECTURE.md}] Architektur von
  \texttt{osglecture};
\item[\texttt{development-docs/GLOSSARY.md}] verbindliche Begriffe;
\item[\texttt{development-docs/DEPENDENCIES-ollm.md}] technische Herkunft
  und Reparaturhinweise zu Laufzeitabhängigkeiten;
\item[\texttt{development-docs/DEVELOPMENT-ollm.md}] Build-, Test- und
  Paketierungshinweise für Mitwirkende;
\item[\texttt{ollm/THIRD\_PARTY.md}] Lizenzen mitgelieferter Komponenten.
\end{description}

\end{document}


% ## Implementation status and TODO

% The new executor currently implements ordinary and continuous `build`,
% `build --all`, `build --resolve`, `build --dry-run`, standalone builds, and
% the global and project-aware `doctor` toolchain check. Native latexmk clean
% and information actions can be passed through to one resolved build.

% `report` describes discovered units, promoted projections, their generations,
% and semantic dependencies without using findings as a release gate. `check`
% uses the same analysis and returns exit code 3 for inconsistent required
% dependencies. A discovered but unused and unbuilt unit is reported as
% `dormant` and is not a check failure. At the project root both commands default
% to `series`; inside a unit they default to `current`. `unit` is always
% explicit.

% `clean` implements the documented `aux`, `build`, `state`, and `all` levels
% with `current`, `unit`, and `series` scopes. `prune` removes abandoned pending
% directories and superseded immutable generations. Potentially renamed units
% are reported but retained unless `--stale-units` is explicit. Both commands
% support `--dry-run`.

% `deploy` copies already promoted PDF artifacts; it never starts an implicit
% build. Its default scope is `series` at the project root and `current` inside
% any unit. `--scope=collection` selects the integration artifact for a document
% type. Missing destinations are not created and produce a failing status; later
% attempts to the same unavailable path are skipped while other destinations
% continue. Existing different files follow
% `security.deployment.overwrite = "explicit" | "automatic"`; the explicit
% policy requires `--overwrite`. Installation uses a temporary file in the
% destination and prefers an atomic rename.

% Series builds now assign a generation ID, validate the LaTeX result and
% reference envelopes, and atomically promote an immutable generation for each
% logical unit/document type/language projection. Before a following build OLLM
% writes a job-bound TeX registry of the last valid projections. The logical
% unit ID always comes from the explicit `\lecture` declaration; a directory
% slug is never promoted as its substitute.

% The `.osglecture` tree is OLLM-owned but deliberately inspectable and fully
% rebuildable. With no OLLM or latexmk process running, deleting it is a
% supported radical recovery procedure; this also discards all promoted logical
% mappings, so target units must be built directly again. Backend-specific
% optional-tool checks and TeX-side reporting of effective and enforced
% configuration values remain TODO.

% ## TOML parser

% OLLM ships the read-only parser from `TOML::Tiny` 0.22. No CPAN module has to
% be installed by users. `ollm doctor` reports the parser name, version, and
% origin.

% The bundled copy is intentional: TeX Live does not guarantee third-party Perl
% modules, and its Windows Perl installation is deliberately minimal. Provenance
% and licensing are recorded in [`THIRD_PARTY-ollm.md`](THIRD_PARTY-ollm.md).
% Detailed fallback instructions, including the TeX Live setting for an external
% Perl on Windows, are in [`DEPENDENCIES.md`](DEPENDENCIES.md).

% Developers who want a separate upstream installation for comparison can use:

% ```sh
% cpanm TOML::Tiny
% ```

% or, with the CPAN client included in many Perl installations:

% ```sh
% cpan TOML::Tiny
% ```

% OLLM itself continues to use its tested bundled parser; installing the CPAN
% distribution is therefore optional and does not repair or change an OLLM
% installation.

% ## Project manifest

% The manifest is named `ollmconfig.toml` and marks the project root. OLLM
% searches for it from the working directory upwards. An explicit manifest or
% project root can be selected with:

% ```sh
% ollm build script --config=/path/to/ollmconfig.toml --dry-run
% ollm build script --project-root=/path/to/project --dry-run
% ```

% For migration, `ollmconfig.pl` may coexist with the TOML manifest; builds
% use TOML only. If only the
% Perl file exists, `ollm convertproject` creates a conservative TOML translation
% without executing arbitrary Perl and imports recognizable metadata from
% `Include/lectdates.tex` into `Include/projectconfig.tex`. `ollm newproject`
% creates a generic manifest and project configuration, or performs that
% conversion when it discovers legacy files. Neither command overwrites an
% existing configuration file. Statically recognizable legacy deployment
% rules are translated; unsupported dynamic, restriction, path, or computed
% settings are reported for manual review.

% Commands and built-in targets accept the historical leading `+`. With
% `+enforce+` or `--enforce+`, that prefix becomes mandatory for command and
% target words, allowing files named for example `slides` to remain operands.
% `+force+` is retained as a deprecated compatibility alias.

% Deployment rules are optional and apply to generated artifacts only:

% ```toml
% [deployment]
% series = "both" # units | collection | both

% [deployment.roles]
% content = ""
% appendix = "A"

% [deployment.types.handout]
% paths = ["/srv/lecture/handouts", "/srv/lecture/archive"]
% filename = "{role}{chapter:02}-{unit}-{lang}.pdf"
% collection_filename = "{series}-{lang}.pdf"

% [deployment.types.handout.units.introduction]
% filename = "{chapter:02}-intro-{lang}.pdf"

% [security.deployment]
% overwrite = "explicit"
% ```

% Filename templates support `series`, `unit`, `slug`, `ordinal`, `chapter`,
% `doctype`, `lang`, and mapped `role` placeholders. `slug` is the descriptive
% part of the unit directory name, which is useful when the logical unit ID is
% written as a label such as `un:processes`. Decimal values accept a minimum width,
% for example `{ordinal:02}`. LaTeX reports the actual chapter representation;
% `\OsgLectureDeploymentChapter{...}` overrides it explicitly. A generic
% `newproject` file contains commented deployment examples. `convertproject`
% translates statically recognizable legacy `deploy_path` and `deploy_file`
% assignments and reports unsupported dynamic or restriction settings.

% Bundle presets and targets are resolved from versioned definitions shipped
% with the bundle. Additional absolute or project-root-relative search paths can
% be put in `.ollmconfig.local.toml`:

% ```toml
% schema = 1

% [definitions]
% paths = ["configuration", "/opt/osglecture/definitions"]
% ```

% Unknown standard keys, bundle presets, and targets are errors. Semantic
% diagnostics include the configuration file and source line whenever the
% offending key is present in the TOML source. The bundle-level example using
% a custom target definition, an exercise sheet with a `solutions` version, is
% in [`../examples/series-modern`](../examples/series-modern).

% Targets registered by additional definitions can be selected explicitly:

% ```sh
% ollm build --target=studyguide --language=en
% ```

% A schema-1 target definition uses the same `name` and `doctype` and declares
% `profile_class = "presentation"` or `profile_class = "longform"`. This value
% selects the corresponding project-wide document profile unless
% `\LectureTargetSetup` supplies a more specific TeX-side selection. It also
% declares a `document_metadata` default. Mode parents
% and abstract modes belong to that profile's TeX-side `mode-setup-file`; OLLM
% does not merge or interpret the mode graph.

% Project-wide TeX material lives outside the project root's top level. The
% default directory and project configuration are `Include` and
% `projectconfig.tex`; both names can be changed in the project manifest:

% ```toml
% [project.tex]
% directory = "Include"
% config = "projectconfig.tex"
% ```

% The directory is project-root-relative. OLLM adds its resolved absolute path
% to `TEXINPUTS`, after the isolated build directory and before inherited search
% paths, so it may contain project-local packages as well as configuration
% files. If the configured project configuration exists, its contents
% participate in the BuildSpec signature. The generated job-bound build file
% passes its absolute directory and filename to `osglecture`.

% `osglecture` loads that file after the active mode graph and its metadata
% interface have been initialized, but before the remaining class services and
% profile setup. Mode-specific metadata is therefore valid, for example:

% ```latex
% \author<presentation>[M.~M.]{Max Mustermann}
% \author<longform>[Mustermann]{Max Mustermann}
% ```

% Project titles and language-variant mappings are TeX properties rather than
% manifest values. Put the title and the `langselect` mapping in TeX
% configuration; a standalone document can input the same file explicitly:

% ```latex
% \title{Operating Systems}
% \usepackage[
%   languages={de,en},
%   targetlang={\OsgLectureLanguage},
%   map={de=ngerman,en=british}
% ]{langselect}
% ```

% The TOML `[project]` table therefore contains only the technical `id` and its
% optional `tex` locator. `[languages]` contains only the build-matrix values
% `available` and `default`.

% For a concrete series build, the resolved configuration is normalized into a
% job-bound `<jobname>.osgbuild.tex`. The reader in
% `../osglecture/osglecture-config.sty` validates its schema and requires its
% `job-id` to match TeX's current job name. OLLM identifies that concrete file
% before the class is loaded; the reader performs no filename or timestamp
% search. `--dry-run --format=json` exposes the same normalized `build_spec`
% without writing or starting LaTeX.

% The per-user configuration selects only a bundle preset:

% ```toml
% schema = 1
% bundle_preset = "OSG lecture/1"
% ```

% Each target definition supplies a `document_metadata = "required" | "disabled"`
% default, which `[targets.<name>]` may override. For `required`, OLLM requires
% `documentmetadata.tex` in the configured shared TeX directory and inserts it
% before the main source through latexmk's controlled pre-TeX mechanism. For
% `disabled`, an existing file is not read. Immediately before the main source
% OLLM defines the normalized requested target, doctype, profile class, metadata
% policy, and language as `\OsgLectureRequested...` symbols.
% The user-owned file normally contains the actual `\DocumentMetadata{...}` and
% may use that symbol, directly or through `langselect`, for the target language.
% Its contents participate in the configuration signature. OLLM does not inspect
% or rewrite `main.tex`; a second `\DocumentMetadata` remains a normal LaTeX
% conflict. User `-pretex` and `-usepretex` options are reserved and rejected.

% OLLM reads `$XDG_CONFIG_HOME/ollm/config.toml`, or
% `$HOME/.config/ollm/config.toml` when `XDG_CONFIG_HOME` is unset. On Windows it
% uses `%APPDATA%\ollm\config.toml`. `OLLM_USER_CONFIG` selects an explicit file,
% which is also useful for testing. Without a file OLLM uses `OSG lecture/1`.

% The target contributes only its abstract `profile_class` (`presentation` or
% `longform`) to the BuildSpec. The concrete TeX profile is selected by
% `presentation-profile`, `longform-profile`, or the more specific
% `\LectureTargetSetup{<target>}{profile=...}` in `projectconfig.tex`; it is not
% an OLLM or CLI setting. This keeps presentation policy on the TeX side and
% allows a future non-TeX pipeline to use the same target contract.

% ## Build execution

% Each configured target/language pair is normalized to a concrete BuildSpec.
% The executor loads only the bundled `ollm-latexmk.rc`; personal and directory
% `latexmk` configuration files do not alter the controlled build contract.
% Series builds use:

% ```text
% <project-root>/.osglecture/build/<physical-unit>/<target>/<language>/
% ```

% for their PDF, recorder data, `latexmk` state, auxiliary files, and the
% job-bound `<jobname>.osgbuild.tex`. OLLM starts `latexmk` with LuaLaTeX,
% recorder mode, SyncTeX, an explicit job name, and controlled output paths.
% Before the source it defines `\OSGLectureProjectManifestFile` and
% `\OSGLectureJobFile` with the concrete normalized paths. The class checks that
% the manifest exists and loads only the selected job file; it never chooses a
% job file by name search or modification time. Absolute paths are an executor
% choice for reliable `-cd` and parallel operation, not a requirement on other
% runners: any unambiguous TeX-resolvable locator satisfies the class contract.

% Every series resolution also computes a path-independent signature of the
% ordered physical unit structure. The signature is written to the BuildSpec and
% its `.osgbuild.tex` file, so a manual unit rename or reordering invalidates
% affected builds without guessing a logical identity. Unrelated directories,
% timestamps, and the absolute project location do not affect the signature.
% Future Cargo-like `ollm init` and `ollm unit ...` commands may make structural
% changes more convenient, but ordinary filesystem operations remain supported.

% `+standalone` uses the new executor without manifest discovery, a generated
% `.osgbuild.tex`, or a series job name. It defaults to `main.tex` in the current
% directory and passes normal latexmk output-directory options (`-outdir`,
% `-auxdir`, and `-out2dir`). Document type, language, and profile belong in
% `osglecture` class options in this mode.

% The manifest policy maps directly to the LuaLaTeX engine:

% ```text
% off         --no-shell-escape
% restricted  --shell-restricted
% full        --shell-escape
% ```

% If `[security].shell_escape` is omitted, the new executor uses `restricted`.
% Conflicting user-supplied output, job-name, engine, recorder, working-directory,
% or shell-escape options are rejected rather than silently overriding the
% BuildSpec.

% Additional `latexmk` rc files and startup Perl code (`-r`, `-e`) are rejected,
% as are non-LuaLaTeX engines, injected `-latexoption` values, and `-use-make`.
% For series builds, `-out2dir` is also rejected because OLLM owns the artifact
% path. It is permitted only for standalone builds, where normal latexmk output
% directories remain under user control.

% Native `latexmk` clean and information actions such as `-c`, `-C`, `-help`,
% and `-version` are passed through. OLLM does not require a PDF after such an
% action. Options that still request an OLLM build or build matrix, such as
% `--rebuild` or `--all`, are rejected when they contradict the selected
% `latexmk` action. A future `ollm clean` implementation may expose the
% target-scoped `latexmk` cleanup through OLLM's own level and scope model.

% `--all` creates one BuildSpec for each configured target/language pair that
% applies to the current unit scope. Target definitions declare this
% applicability through `unit_scopes`.

% ## Continuous builds and viewers

% OLLM delegates continuous compilation and preview to `latexmk`. Options such
% as `-pvc`, `-cc`, `-pv`, and `-view=none` are passed through. Continuous mode
% is restricted to one concrete BuildSpec because its `latexmk` process remains
% active; combining it with `--all` is therefore an error. Viewer or print
% actions are rejected under `--non-interactive`, while `-pvc -view=none` and
% `-cc` remain valid headless continuous-build modes.

% OLLM normalizes the manifest and writes the build-request file before starting
% `latexmk`. Changes to `ollmconfig.toml`, local configuration, bundle presets,
% or target definitions therefore require restarting OLLM. LaTeX source changes
% continue to be handled normally by `latexmk`.

% Interrupting a build is passed through as a conventional signal exit status;
% in particular, Ctrl-C results in exit code 130 on platforms using POSIX wait
% status.

% ## Path and process robustness

% Series units and project roots are canonicalized before a BuildSpec is
% created. A series unit must remain inside its project root. Build-state paths
% are checked again before directory creation so that a symlinked
% `.osglecture` directory cannot redirect writes outside the project.

% Job identities, configured languages, targets, and build directories are
% checked for collisions on case-insensitive filesystems even when OLLM runs on
% a case-sensitive Unix filesystem. Paths containing spaces remain individual
% process arguments. A build directory containing the platform's `TEXINPUTS`
% list separator is rejected because kpathsea cannot represent it
% unambiguously.

% Each concrete BuildSpec owns a `.ollm.lock` in its isolated build directory.
% The lock is held for the complete `latexmk` lifetime, including continuous
% mode. A second process targeting the same BuildSpec fails before writing or
% starting LaTeX. Different BuildSpecs share no writable registry or state file,
% so later parallel execution does not require changing this ownership model.

% Nonzero `latexmk` exits map to OLLM's build-failure exit code 1; tool-start
% failures remain environment error 69, while signal exits retain their
% conventional `128 + signal` status. A successful ordinary build additionally
% requires a nonempty regular PDF at the exact artifact path.
