// Simple numbering for non-book documents #let equation-numbering = "(1)" #let callout-numbering = "1" #let subfloat-numbering(n-super, subfloat-idx) = { numbering("1a", n-super, subfloat-idx) } // Theorem configuration for theorion // Simple numbering for non-book documents (no heading inheritance) #let theorem-inherited-levels = 0 // Theorem numbering format (can be overridden by extensions for appendix support) // This function returns the numbering pattern to use #let theorem-numbering(loc) = "1.1" // Default theorem render function #let theorem-render(prefix: none, title: "", full-title: auto, body) = { if full-title != "" and full-title != auto and full-title != none { strong[#full-title.] h(0.5em) } body } // Some definitions presupposed by pandoc's typst output. #let content-to-string(content) = { if content.has("text") { content.text } else if content.has("children") { content.children.map(content-to-string).join("") } else if content.has("body") { content-to-string(content.body) } else if content == [ ] { " " } } #let horizontalrule = line(start: (25%,0%), end: (75%,0%)) #let endnote(num, contents) = [ #stack(dir: ltr, spacing: 3pt, super[#num], contents) ] #show terms.item: it => block(breakable: false)[ #text(weight: "bold")[#it.term] #block(inset: (left: 1.5em, top: -0.4em))[#it.description] ] // Some quarto-specific definitions. #show raw.where(block: true): set block( fill: luma(230), width: 100%, inset: 8pt, radius: 2pt ) #let block_with_new_content(old_block, new_content) = { let fields = old_block.fields() let _ = fields.remove("body") if fields.at("below", default: none) != none { // TODO: this is a hack because below is a "synthesized element" // according to the experts in the typst discord... fields.below = fields.below.abs } block.with(..fields)(new_content) } #let empty(v) = { if type(v) == str { // two dollar signs here because we're technically inside // a Pandoc template :grimace: v.matches(regex("^\\s*$")).at(0, default: none) != none } else if type(v) == content { if v.at("text", default: none) != none { return empty(v.text) } for child in v.at("children", default: ()) { if not empty(child) { return false } } return true } } // Subfloats // This is a technique that we adapted from https://github.com/tingerrr/subpar/ #let quartosubfloatcounter = counter("quartosubfloatcounter") #let quarto_super( kind: str, caption: none, label: none, supplement: str, position: none, subcapnumbering: "(a)", body, ) = { context { let figcounter = counter(figure.where(kind: kind)) let n-super = figcounter.get().first() + 1 set figure.caption(position: position) [#figure( kind: kind, supplement: supplement, caption: caption, { show figure.where(kind: kind): set figure(numbering: _ => { let subfloat-idx = quartosubfloatcounter.get().first() + 1 subfloat-numbering(n-super, subfloat-idx) }) show figure.where(kind: kind): set figure.caption(position: position) show figure: it => { let num = numbering(subcapnumbering, n-super, quartosubfloatcounter.get().first() + 1) show figure.caption: it => block({ num.slice(2) // I don't understand why the numbering contains output that it really shouldn't, but this fixes it shrug? [ ] it.body }) quartosubfloatcounter.step() it counter(figure.where(kind: it.kind)).update(n => n - 1) } quartosubfloatcounter.update(0) body } )#label] } } // callout rendering // this is a figure show rule because callouts are crossreferenceable #show figure: it => { if type(it.kind) != str { return it } let kind_match = it.kind.matches(regex("^quarto-callout-(.*)")).at(0, default: none) if kind_match == none { return it } let kind = kind_match.captures.at(0, default: "other") kind = upper(kind.first()) + kind.slice(1) // now we pull apart the callout and reassemble it with the crossref name and counter // when we cleanup pandoc's emitted code to avoid spaces this will have to change let old_callout = it.body.children.at(1).body.children.at(1) let old_title_block = old_callout.body.children.at(0) let children = old_title_block.body.body.children let old_title = if children.len() == 1 { children.at(0) // no icon: title at index 0 } else { children.at(1) // with icon: title at index 1 } // TODO use custom separator if available // Use the figure's counter display which handles chapter-based numbering // (when numbering is a function that includes the heading counter) let callout_num = it.counter.display(it.numbering) let new_title = if empty(old_title) { [#kind #callout_num] } else { [#kind #callout_num: #old_title] } let new_title_block = block_with_new_content( old_title_block, block_with_new_content( old_title_block.body, if children.len() == 1 { new_title // no icon: just the title } else { children.at(0) + new_title // with icon: preserve icon block + new title })) align(left, block_with_new_content(old_callout, block(below: 0pt, new_title_block) + old_callout.body.children.at(1))) } // 2023-10-09: #fa-icon("fa-info") is not working, so we'll eval "#fa-info()" instead #let callout(body: [], title: "Callout", background_color: rgb("#dddddd"), icon: none, icon_color: black, body_background_color: white) = { block( breakable: false, fill: background_color, stroke: (paint: icon_color, thickness: 0.5pt, cap: "round"), width: 100%, radius: 2pt, block( inset: 1pt, width: 100%, below: 0pt, block( fill: background_color, width: 100%, inset: 8pt)[#if icon != none [#text(icon_color, weight: 900)[#icon] ]#title]) + if(body != []){ block( inset: 1pt, width: 100%, block(fill: body_background_color, width: 100%, inset: 8pt, body)) } ) } // arxivai — Quarto Typst template partial. // Replaces Quarto's default `article()` with the arxivai preprint layout. // Both packages are importable from raw ```{=typst}``` blocks in the body: // #arxivai.operation-box(...) #arxivai.app-figure(...) #lettreai.badge(...) #import "lettreai.typ" as lettreai #import "arxivai.typ" as arxivai // --------------------------------------------------------------------------- // Re-theme Quarto's automatic callouts (same contract as lettreai: block // nesting kept structurally identical to Quarto's default so the crossref // figure show rule in definitions.typ keeps working). // --------------------------------------------------------------------------- #let callout( body: [], title: "Callout", background_color: rgb("#dddddd"), icon: none, icon_color: black, body_background_color: white, ) = { let accent = if icon_color == black { lettreai.colors.oxford-blue } else { icon_color } block( breakable: false, fill: lettreai.tint(accent, 0.94), stroke: (left: 3pt + accent, rest: 0.5pt + lettreai.tint(accent, 0.5)), width: 100%, radius: 3pt, block( inset: 1pt, width: 100%, below: 0pt, block( fill: lettreai.tint(accent, 0.84), width: 100%, inset: 8pt, )[#if icon != none [#text(accent, weight: 900)[#icon] ]#text(fill: lettreai.shade(accent, 0.25), weight: 700)[#title]]) + if (body != []) { block( inset: 1pt, width: 100%, block(fill: body_background_color, width: 100%, inset: 8pt, body)) } ) } // --------------------------------------------------------------------------- // arXiv preprint article — same signature contract as Quarto's default, // extended with keywords and running-title. // --------------------------------------------------------------------------- #let article( title: none, subtitle: none, authors: none, keywords: (), running-title: none, book: false, date: none, abstract-title: none, abstract: none, thanks: none, cols: 1, lang: "en", region: "US", font: none, fontsize: 10.5pt, title-size: 1.7em, subtitle-size: 1.15em, heading-family: none, heading-weight: "bold", heading-style: "normal", heading-color: black, heading-line-height: 0.65em, mathfont: none, codefont: none, linestretch: 1, sectionnumbering: none, linkcolor: none, citecolor: none, filecolor: none, toc: false, toc_title: none, toc_depth: none, toc_indent: 1.5em, doc, ) = { // PDF metadata. set document(title: title) set document( author: authors.map(a => lettreai.to-string(a.name)).join(", ", last: " & "), ) if authors != none and authors != () set document( keywords: keywords.map(k => lettreai.to-string(k)), ) if keywords != none and keywords != () set par(justify: true, leading: linestretch * 0.65em) set text(lang: lang, region: region, size: fontsize) show math.equation: set text(font: mathfont) if mathfont != none set heading(numbering: sectionnumbering) // Running header + footer; book mode gets chapter-aware heads and the // O'Reilly outer-margin geometry. let rt = if running-title != none { running-title } else if title != none { title } else { [] } set page( header: if book { arxivai.book-header() } else { arxivai.arxiv-header(rt) }, footer: if book { arxivai.book-footer() } else { arxivai.arxiv-footer() }, ) set page(margin: arxivai.book-margin) if book // Resolve fonts: explicit Quarto fonts win, else theme defaults. let resolved-font = if font != none { font } else { lettreai.body-font } let resolved-code = if codefont != none { codefont } else { lettreai.mono-font } let resolved-heading-color = if heading-color != black { heading-color } else { lettreai.colors.oxford-blue } let resolved-link = if linkcolor != none { rgb(lettreai.to-string(linkcolor)) } else { lettreai.colors.oxford-blue } // Paint everything with the shared theme, then the figure rules. show: lettreai.theme-rules.with( fonts: resolved-font, code-font: resolved-code, heading-color: resolved-heading-color, link-color: resolved-link, ) show: arxivai.arxiv-figure-rules // Book chapter rule is the innermost show, so it transforms level-1 // headings FIRST (inner rules apply first), replacing them with chapter // openers before the theme's h1 rule could wrap them in a container. // Synthetic non-outlined headings (e.g. the outline's own title) are // excluded inside book-heading-rules. show: doc2 => if book { arxivai.book-heading-rules(doc2) } else { doc2 } show ref: set text(fill: rgb(lettreai.to-string(citecolor))) if citecolor != none // Preprint front matter. arxivai.arxiv-title-block( title: title, subtitle: subtitle, authors: authors, date: date, ) if abstract != none { arxivai.arxiv-abstract( abstract, abstract-title: if abstract-title == none { [Abstract] } else { abstract-title }, ) } arxivai.arxiv-keywords(keywords) // Book mode: the front matter is a title page of its own. if book { pagebreak(weak: true) } // Optional table of contents (its own page in book mode). if toc { block(above: 0.5em, below: 2em, lettreai.lettreai-outline( title: if toc_title == none { auto } else { toc_title }, depth: toc_depth, indent: toc_indent, )) if book { pagebreak(weak: true) } } if cols > 1 { columns(cols, doc) } else { doc } } #import "@preview/fontawesome:0.5.0": * #let brand-color = (:) #let brand-color-background = (:) #let brand-logo = (:) #set page( paper: "a4", margin: (x: 2.4cm,y: 2.6cm,), numbering: "1", columns: 1, ) #show: doc => article( title: [The AI Partitur --- A Score Language for the Humanities], subtitle: [Orchestration one can read: agents as movements, YAML as notation], authors: ( ( name: [Gerd Graßhoff], affiliation: [Humboldt-Universität zu Berlin, Deutsches Museum], email: [gerd.grasshoff\@hu-berlin.de], orcid: [0000-0002-4450-357X] ), ), keywords: ([digital humanities],[agent orchestration],[partitur],[YAML],[literate programming],[AI Erasmus],[declarative workflows],[computational epistemology],), running-title: [The AI Partitur], abstract: [Computational workflows in the humanities are frequently obscured by imperative programming logic: control flow, error handling, retry policies, and heuristic thresholds remain distributed across source code that only programmers can inspect and modify. This paper presents the #emph[AI Partitur] --- a declarative score language for agent orchestration that removes this barrier by replacing opaque code paths with an editable, inspectable document. In this system, every autonomous agent is defined by a single YAML document (its score), executed by a runtime engine called the Conductor. All parameters critical to scholarly validity --- goal decompositions, prompt compositions, pass thresholds, retry bounds, iron editorial rules, and learning targets --- are declared as explicit fields rather than buried in code. Drawing on musical notation, Donald Knuth's literate programming, James Grier's critical editing of scores, and Ludwig Wittgenstein's use-theory of rules, we demonstrate how complex research workflows decompose into movements, AND-OR goal trees, predicate guards, and rehearsal loops. We walk through the production score `ai-query-phase1` from AI Erasmus, show the contracts by which a stage declares what its result and its grounds must contain, exhibit nested sub-agent scores for modular methodology, detail the Harvest learning loop for benchmark-driven rehearsal, and present a typographic display language for citing orchestration in scholarly print. The resulting architecture turns the domain scholar from a passive consumer of software features into an active composer and editor of research instruments. ], abstract-title: "Abstract", font: ("Source Sans 3",), sectionnumbering: "1.1", toc: true, toc_title: [Table of contents], toc_depth: 3, doc, ) = Introduction: The Challenge of Computational Notation == Programs as Opaque Mechanisms For more than half a century, scholars in the humanities have been urged to learn to program. The prescription, however, routinely misdiagnosed the true obstacle. The fundamental friction between philologists, historians, and philosophers on the one hand, and computational systems on the other, was never an aversion to formal rigor; it was the #emph[notation] in which computational behavior is traditionally expressed. In imperative programming languages, operational logic is dispersed across files, classes, and helper functions. Control flow is buried in loops and branching statements; editorial assumptions are hardcoded as arbitrary constant values; and procedural dependencies are intelligible only to those who can trace dynamic execution stacks. When a historian needs to verify whether a correspondence search excludes undated drafts, or a philologist wishes to adjust the confidence threshold of a lemma match, they are forced to confront software engineering machinery unrelated to their domain inquiry. If a pipeline must be adapted, the domain scholar depends on a software specialist to locate and modify hidden lines of code. This division of labor creates an epistemic disconnect: the scholar remains responsible for the validity of the final claim, yet cannot inspect or audit the procedural mechanics that produced the evidence. == The Declarative Score as Inspectable Method The AI Partitur resolves this disconnect by changing the representation of orchestration. In the AI Agent Orchestrator framework @orchestrator2026, every agent is defined by a single, self-contained YAML document: its #emph[score] (German #emph[Partitur]). A dedicated runtime engine, the #emph[Conductor], parses, validates, and executes this document. The governing design principle is uncompromising: #emph[everything that governs scholarly judgment is declared in the Partitur]. #arxivai.margin-note[The German #emph[Partitur] denotes the full orchestral score: all voices and instruments aligned simultaneously on one page across time.] If an operational parameter affects research results --- a decomposed goal, a prompt template, an evaluation metric, a loop bound, a routing guard, a millisecond latency budget, or a learning target --- it is recorded as an explicit field in the YAML score, not as a variable in a programming script. Scholars configure, audit, and refine agents by reading and editing declarative markup. This approach builds on Donald Knuth's concept of #emph[literate programming] @knuth1984, in which programs are structured as works of literature intended for human understanding rather than merely instructions for compilers. It also connects directly with James Grier's principles of the critical editing of musical scores @grier1996 and Ludwig Wittgenstein's analysis of rule-following and notation @wittgenstein1953: a score serves as an explicit, citable rule of performance that permits systematic critique and verifiable compliance. == Three Core Claims Three foundational claims organise the argument of this paper: + #strong[Readability.] A Partitur can be read and understood by scanning its structural hierarchy first, because its musical vocabulary is exact rather than metaphorical (Chapters 2 and 3). + #strong[Scholarly operations run on scores.] The query and indexing pipelines of the AI Erasmus platform @grasshoff2026aierasmus are played directly from executable scores, complete with editorial iron rules, deterministic backends, and predicate-gated fallbacks (Chapters 4 and 5), refined by automated rehearsal loops (Chapter 6). + #strong[Scores can be quoted and cited in print.] Scholarly publication requires a typographic standard to quote orchestration the way musicology quotes bars; Chapter 7 demonstrates the display language developed for this purpose. The paper concludes by analyzing how this shift redefines the humanist as a composer and editor of computational research instruments (Chapter 8), situates the Partitur within the broader AI Platform architecture (Chapter 9), and outlines its verified status (Chapter 10). = Anatomy of a Partitur: Identity, Structure, and Stages == Top-Level Declarations and the Goal Tree A musical score is read from global structure to individual voices: how many movements comprise the work, in what order they proceed, and how they relate. A Partitur provides the same structural clarity. Its top-level mapping declares a unique identifier, an overarching natural-language goal, a default runtime instrumentation, and an explicit #emph[goal tree]. #arxivai.score-box( "partitur: id: research-report goal: \"Produce a short, sourced report on a given topic.\" runtime: mock goal_tree: op: AND children: [gather, write] entry: gather", id: "research-report", path: "top level", marks: ( (line: 4, note: [Instrumentation: declares the default execution backend. The #raw("mock") provider enables deterministic, offline testing; single-line edits swap in local Ollama or hosted model backends.]), (line: 6, note: [Goal tree structure: an AND node specifies that the agent succeeds if and only if #emph[both] constituent movements succeed.]), (line: 8, note: [Entry movement: identifies the initial stage where the Conductor commences execution.]), ), plays-on: "Conductor / mock", ) The top level defines the complete macro-structure of the agent. The agent `research-report` consists of two sequential movements, `gather` and `write`. They are conjoined by an `AND` operator in the `goal_tree`, declaring that failure of either movement constitutes failure of the overarching task. The Conductor begins execution at the `gather` stage. == Stage Definition and Semantic Fields Each individual movement --- termed a #emph[stage] --- carries its own localized goal, instruction context, runtime overrides, grading evaluators, iteration bounds, and routing guards. The stage definition below shows the `write` movement of the `research-report` score: #arxivai.score-box( "- id: write goal: \"Write a clear, well-structured, sourced report.\" instruction: context: [topic, web] skills: [report-writing] loop: { max_iterations: 4, until: \">=GOOD\" } guards: on_grade: { \">=GOOD\": done, \"=GOOD"` express termination conditions in the qualitative grading vocabulary of the evaluation framework rather than low-level integer or boolean comparisons. - #strong[Declarative state routing.] The `guards.on_grade` table specifies transitions as a deterministic state machine, separating workflow logic from the prompts or models that generate output. == Validation at Load Time Unlike interpreted scripts that fail midway through execution when an unbound variable is encountered, the Conductor validates every Partitur structurally before execution begins @orchestrator2026. The validating loader enforces four invariants: + #strong[Closed vocabulary.] A score and each of its stages admit only declared fields; an unknown or misspelled key is an error, not a silently ignored line. (Entries of a `learning:` block are the exception: unknown keys there are ignored.) + #strong[Identifier uniqueness.] All stage identifiers within a score must be unique. + #strong[Graph completeness.] Every target named in `entry`, `next`, a guard action, a loop's `on_exhausted`, a composite's `children`, or the `goal_tree` must resolve to a stage definition or a reserved keyword (`next`, `done`, `fail`, `retry`, `self`). + #strong[Ordered dependencies.] A stage may declare in `depends_on` only stages that come before it in the score, so the declared data flow cannot run backwards. If any invariant is breached, the Conductor rejects the entire document at load time, emitting a diagnostic that names the stage and the broken reference. Two further properties are not proved at load time, and the score's reader should know which. Guard routing may form cycles; they are bounded at run time, by each `loop`'s `max_iterations` and by a hard ceiling of 200 steps per run, beyond which the Conductor stops with an error. And a context key that no earlier stage has published is skipped when the prompt is composed; `orchestrator validate --strict` reports template variables that the score does not declare. == Contracts a Stage Must Meet Grades say how well a stage performed; a contract says what its result must contain. Three stage fields form the contract: `result_spec` names the published result and the fields it must have, `verification` lists rules over the output and the blackboard, and `justification_spec` names where the result's grounds lie, how many are required, and which fields every ground must carry. The stage below, from the score that sets out the argument of an answer in the Famulus justification layer @grasshoff2026famulusreasoning, declares all of it: #arxivai.score-box( "- id: begruenden goal: Aussagen, Prämissen, Gründe, Einwände, Urteil. runtime: famulus:anfrage_begruendung result_spec: key: begruendung format: {urteil: str, aussagen: list, gruende: list, einwaende: list} justification_spec: gruende: begruendung.gruende mindestens: 1 felder: [address, excerpt] wenn: \"state['begruendung']['urteil'] == 'validiert'\"", id: "anfrage-begruendung", path: "begruenden", marks: ( (line: 4, note: [Result contract: the published #raw("begruendung") must carry a verdict, the propositions, the grounds, and the objections.]), (line: 7, note: [Justification contract: at least one ground, and every ground with an address in the evidence and an excerpt.]), (line: 11, note: [Condition: the justification contract binds when the verdict is #emph[validiert] — a verdict without grounds is not accepted as one.]), ), plays-on: "Conductor / famulus:anfrage_begruendung", ) The Conductor checks the contract on every attempt and records the outcome in the trace. It fails the attempt only when the score opts in with `metadata.contract: enforce`, because older scores carry verification blocks that never graded a run. In October 2026 eighteen of the seventy-five scores in the orchestrator's agent store enforce their contracts, all of them in the Famulus justification layer. For those scores, that an answer has its grounds is part of the score, not a property of the code behind it. #block[ #callout( body: [ The Conductor runtime engine that loads, validates, and executes scores is written in standard software code. The claim of the Partitur is not that underlying software is eliminated, but that the #emph[entire editable surface affecting scholarly interpretation] is elevated into declarative notation. The scholar edits and inspects the score; the system engineer maintains the Conductor instrument. ] , title: [ What Remains Code: The Conductor Runtime ] , background_color: rgb("#dae6fb") , icon_color: rgb("#0758E5") , icon: fa-info() , body_background_color: white ) ] = The Musical Vocabulary: Construct-by-Construct Correspondence == Mapping Table and Semantic Justification Software projects frequently employ metaphors casually. In the Partitur architecture, the musical vocabulary maps directly and rigorously to computational constructs, as documented in #ref(, supplement: [Table]). #figure([ #table( columns: (33.33%, 33.33%, 33.33%), align: (auto,auto,auto,), table.header([Musical Term], [Partitur Construct], [Computational Meaning],), table.hline(), [Score (#emph[Partitur])], [YAML Document], [Complete declarative specification of agent behavior], [Conductor], [Runtime Engine], [Deterministic loader, validator, and execution engine], [Movement], [Stage (`id`)], [Discrete sub-task with localized goal and context], [The Work's Structure], [Goal Tree (`goal_tree`)], [Hierarchical AND-OR logic over movements], [Repeat Marks / #emph[Da Capo]], [Guard Transitions (`on_grade`)], [Grade-conditioned jump to self or alternative stage], [Bounded Ritornello], [Loop Definition (`loop`)], [Iteration bounded by count and grade criteria], [Instrumentation], [Runtime Specification (`runtime`)], [Execution engine binding (DuckDB, Ollama, API model)], [Rehearsal], [Learning Loop (`learning`)], [Benchmark-driven optimization of skills toward goldens], [Silence (played)], [Skip Movement (`pdai:noop`)], [Explicit no-op stage recorded in the audit trace], ) ], caption: figure.caption( position: top, [ The correspondence between musical notation and Partitur constructs. ]), kind: "quarto-float-tbl", supplement: "Table", ) #arxivai.sidebar(title: [Why Exact Notation Matters])[ A casual metaphor decorates; an exact notation instructs. Because the correspondence in @tbl-vocabulary holds construct for construct, a scholar's intuitive understanding of structured scores transfers directly to workflow design. A researcher who understands the instruction "repeat the movement until the rendering is acceptable, up to three attempts" understands the #raw("loop") and #raw("guards") directives without formal training in imperative programming. ] == Defending the Mapping: Instrumentation, Rehearsal, and Silence Three terms in this correspondence require explicit defence: - #strong[Instrumentation.] In music, a melodic line may be reassigned from violins to woodwinds without modifying the notes themselves. In a Partitur, a stage can be switched from a deterministic database query (a DuckDB-backed `pdai:` op) to a local open-weights model (`ollama:qwen3.8:27b-mlx`) or a cloud model (`gemini:gemini-3.8-flash`) by altering a single `runtime:` field. The operational contract and surrounding workflow remain unchanged. - #strong[Rehearsal.] An orchestra does not alter the notes of a symphony during rehearsal; rather, musicians refine their technique against an idealized standard of interpretation. In the Partitur framework, the Harvest learning subsystem optimizes editable skills and prompt instructions against golden reference queries, leaving the structural goal tree intact. - #strong[Silence as an executed movement.] In musical performance, a rest is not an omission but a deliberate event that occupies time. In the Partitur, when a conditional branch finds nothing to process, it executes an explicit `pdai:noop` skip movement. This skip costs approximately one millisecond and is recorded in the execution trace. Silence is actively performed and audited, ensuring that negative evidence is fully documented. Following Wittgenstein's insight that the meaning of a rule is demonstrated by its systematic application @wittgenstein1953, a Partitur provides an explicit rule whose execution trace can be cited as evidence for computational findings. = A Score for Scholarship: The `ai-query-phase1` Pipeline == The Resolution Contract and Editorial Iron Rules The utility of a score language must be established on real research tasks. The primary running example in this paper is `ai-query-phase1`, the Partitur that resolves every scholar's corpus query in the AI Erasmus platform @grasshoff2026aierasmus@grasshoff2026goldenqueries. It began as a score of ten pre-analysis stages, which is why its identifier still reads "phase1"\; the audit tables and the score's goldens are keyed to that name. Today it declares the whole resolution in 47 stages, from the construction of the work context to the final goal check. #arxivai.score-box( "partitur: id: ai-query-phase1 name: grounded-query-resolution goal: > Beantworte eine historische Fachfrage aus belegter Evidenz: … Jede Behauptung im Ergebnis trägt einen Grounding-Locus (collection, doc_id, pidx). # … runtime: pdai:noop # … metadata: goal_path: \"query/historical/stage-one\" # … iron_rules: - \"Collection names are SCOPE (scoping_terms), never lemmas in lemma_ncf\" # … - \"No costly searches of no value — audit every run/skip\" # …", id: "ai-query-phase1", path: "top level", marks: ( (line: 5, note: [The goal, in the score's own German: answer a historical question from attested evidence, and give every claim of the answer a grounding locus.]), (line: 9, note: [The top level does no work itself; every stage names its own runtime.]), (line: 15, note: [Editorial iron rules: domain-specific constraints declared directly in score metadata, preventing common categorization errors.]), ), plays-on: "Conductor / ai-query-phase1", ) The metadata section contains seven #emph[iron rules] --- explicit editorial directives that govern all downstream movements. The first, for example, declares collection names to be scope and never lemmas of the lemma filter: a reference to #emph[Kepler] in a query like "Lex natura in Kepler's writing" restricts the query to the `kepler-prim` corpus rather than being erroneously searched as a Latin co-occurring lemma. The score also states where a language model enters. One instructed decode (`afm-query-extract`, on `gemini-3.8-flash`) reads the question into entity spans and a structured filter record; which kind of question it is then follows from that structure by declared rules, not from the model. The filter funnel that follows --- collection, person, date, lemma, general filter --- runs on DuckDB and the lemma index, and only the small set of passages it leaves is handed to the stages that write the answer. == Declared Budgets and OR Fallbacks The score declares a millisecond budget for each movement in its metadata and orders execution into tiers, the cheapest first: #arxivai.score-box( "- id: stanza-or-skip goal: \"Attest remaining content terms as lemmata — or skip if none.\" goal_tree: op: OR children: [stanza-filter, stanza-skip] - id: stanza-skip goal: \"No content terms left after entity/scope removal — skip stanza probe.\" runtime: pdai:noop", id: "ai-query-phase1", path: "stanza-or-skip", marks: ( (line: 4, note: [OR fallback node: execution succeeds if either the full linguistic filter or the explicit skip movement completes.]), (line: 9, note: [Explicit silence: a #raw("pdai:noop") stage executing in ~1 ms, documenting that lemma attestation was intentionally bypassed.]), ), plays-on: "Conductor / pdai:aiq_stanza_filter", ) #arxivai.margin-note[Declared stage budgets: #raw("budget_ms: 80") for person resolution, #raw("400") for the lemma index probe, #raw("900") for intent classification, which reads the model's decode of the question.] The `OR` node in `stanza-or-skip` establishes a declarative fallback: when entity and scope extraction consume all terms in a query, the pipeline routes to `stanza-skip` rather than executing an expensive, empty database scan. == Predicate-Gated Movements High-cost operations, such as multimodal visual feature detection across scanned manuscripts, are protected by preconditions: #arxivai.score-box( "- id: figure-interpret goal: > Erweitere die Kandidatenmenge um mediale Quellen (Diagramme, Abbildungen) aus den Grounding Sources, wenn die Query explizit nach Figuren fragt … runtime: pdai:aiq_figure_interpret # … when: \"wants_figures\" skip_reason: \"no figure cue in the query\"", id: "ai-query-phase1", path: "figure-interpret", marks: ( (line: 8, note: [Precondition: a bare-name test of the cue flag that the classification stage publishes; if the query does not ask for figures, the stage is skipped without invocation.]), (line: 9, note: [The reason for the skip is declared too, and recorded in the trace.]), ), plays-on: "Conductor / pdai:aiq_figure_interpret", ) By declaring this precondition at the score level, the pipeline guarantees that computer vision models are never triggered unnecessarily on purely textual or biographical questions. The form of the test is itself a lesson of the notation: predicates are evaluated in a sandbox that forbids attribute access, so an earlier version of this guard, written as a method call on the state, raised an error and never fired. The classification stage now publishes its cue flags under bare names, and the guard reads them as such. == From Score to Scholar: The Execution Trace When a scholar submits a question in AI Erasmus, the Conductor records a structured execution trace detailing every movement performed: #arxivai.operation-box( title: [Execution Trace: ai-query-phase1 on Johannes Kepler Corpus], question: [How many letters does KGW 13 contain?], steps: ( (agent: "fasttext-classify", does: [Names the inventory subtask from the decoded structure of the question]), (agent: "collection-select", does: [Binds scope to kepler-prim volume 13; iron rule prevents KGW from being parsed as lemma]), (agent: "volume-letter-count", does: [Scans structured XML Nr. headers and returns unique letter count]), ), answer: [122 letters — counted from the editor's Nr. headers over 446 pages; the numbers run from 1 to 127, and five of them occur in no header. The execution trace records the movements played and the movements skipped], ) The scholar receives not merely an answer, but an inspectable method trace specifying which movements were played, which were skipped, and the exact millisecond performance of each stage. Transparency is an organic byproduct of the score's execution. A trace of method is not yet a record of evidence. Which items an answer read, and in which state of the data, is recorded by the Famulus Reference, the store that gives every page and segment of a collection an `fr:` address: each of its grounding, citation, and SQL calls returns a run identifier that later resolves to the addressed items, the data snapshot, and the SQL used. The Conductor's trace does not yet carry these run identifiers; joining the two records is designed, not implemented. = Scores Within Scores: Subagents and Modularity == Hierarchical Delegation and Subagent Partiturs Complex scholarly inquiries cannot be handled effectively by monolithic, flat workflows. The Partitur architecture supports hierarchical composition through the `partitur:` directive, allowing any stage to delegate its task to a nested sub-score. The deep-reading agent for scientific arguments demonstrates this modular nesting: #arxivai.score-box( "- id: figure_extraction goal: Locate, crop, and caption-pair the paper's figures (…). partitur: id: causal_figure_extractor_subagent # … runtime: mock goal_tree: op: AND children: [figure_locator, figure_cropper, caption_pairing] # … stages: - id: figure_locator goal: Normalized bounding boxes for every figure on every page. - id: figure_cropper goal: Crop and save each located figure. - id: caption_pairing goal: Pair every cropped figure with its verbatim caption and measurement type.", id: "deep-reading-causal", path: "figure_extraction", marks: ( (line: 3, note: [Subagent delegation: embeds a complete child Partitur with its own goal tree and stage definitions.]), (line: 6, note: [Instrumentation: the child score runs offline on the deterministic mock; the parent's metadata names #raw("gemini:gemini-3.8-flash") as the production runtime for figure extraction, a one-line switch.]), (line: 9, note: [Child movements: the sub-task is explicitly decomposed into localization, cropping, and caption alignment.]), ), plays-on: "Conductor / nested sub-score", ) == Method as a Citable, Modular Artifact Hierarchical decomposition offers substantial methodological advantages: - #strong[Granular accountability.] The sub-score `causal_figure_extractor_subagent` can be independently audited, benchmarked, and peer-reviewed without altering the parent reading pipeline. - #strong[Reusability across projects.] The figure extraction score can be linked by different parent agents across distinct scholarly domains (e.g., historical sundial treatises and early modern astronomical prints). - #strong[Targeted peer review.] In scholarly debates over digital methods, disagreements can focus on specific sub-scores --- such as the bounding box heuristic in `figure_locator` --- rather than vague critiques of the pipeline as a whole. = Rehearsal and Refinement: The Harvest Subsystem == Rehearsing Against Golden Benchmarks A score is refined through rehearsal against established benchmarks. In the AI Agent Orchestrator framework, this process is governed by the #emph[Harvest] learning loop subsystem @orchestrator2026. A Partitur declares its rehearsal parameters in an explicit `learning:` block: #arxivai.score-box( "learning: - target: skill:causal-claim-extraction golden: golden:causal-claim-extraction strategy: textual-gradient max_rounds: 4 promote_threshold: BAD", id: "deep-reading-causal", path: "learning", marks: ( (line: 2, note: [Optimization target: identifies the specific skill or instruction file to be refined.]), (line: 3, note: [Golden reference: the target output against which each round's result is graded.]), (line: 4, note: [Optimization strategy: turns the diagnostics of a round into a textual edit signal for the next candidate.]), (line: 6, note: [Promotion bar: a candidate must reach at least this grade to be eligible; a further field, #raw("min_improvement"), sets the score gain it must show.]), ), plays-on: "Harvest Subsystem", ) Rehearsal is opt-in. A declared loop fires after a performance only when the score is played with `orchestrator run --learn`, or when it is run on its own with `orchestrator harvest`\; an ordinary performance does not pay for optimization it did not ask for. == What Rehearsal May Change: Skills versus Structure To maintain epistemic integrity, strict boundaries govern what automated rehearsal may alter: #arxivai.method-box(title: [The Architectural Boundary of Rehearsal])[ Text rehearsal optimizes *skills, instructions, and prompt templates* — and nothing else; it never edits the `goal_tree`, the stage ordering, or the iron rules of a score. Modifying the goal tree changes the research question itself; modifying instructions refines the execution of an established question. A change to the score's structure has its own procedure, *pipeline synthesis*: it proposes a whole new Partitur under a new identifier, never an edit in place, and promotes it only if no golden the incumbent passed now fails, it clears at least as many goldens, and its mean gain exceeds the declared minimum. A synthesis loop declared in a score only banks validated candidates; promotion is a separate, explicit run. ] == Worked Rehearsal Case: Benchmark Optimization The interaction between Partitur scores and rehearsal is shown on a person-mention query from AI Erasmus @grasshoff2026goldenqueries: #emph["Which correspondent mentions Georg Hartmann in his letters to Pirckheimer?"] The query requires the sixteenth-century instrument maker #emph[Georg Hartmann] to be kept apart from Georg Sauermann, a correspondent of the Pirckheimer register who shares his given name; Hartmann himself is no correspondent, has no entry in that register, and is listed only in the edition's person index. ai-query's own learning loop, which applies the same discipline of versioned patches and gated promotion to the instructions of its stages, is the one that has been exercised on it. The monitor log of a run on 6 August 2026 shows the deficit: ``` §4 SIGNALS persons = ['Pirckheimer, Willibald', …, 'Sauermann, Georg'] §5 EXECUTION STREAM run_id = de7b615a0c5040b5 … "letter_ent_ids": {"pirckheimer": ["ent_sauermann_georg"]} … §10 VERDICT answer = count=4 goal_satisfied=True goal_score=0.6 ``` The token #emph[Georg] was bound to the register entry `Sauermann, Georg`, the phrase #emph[Georg Hartmann] was not resolved, and the letter filter was built on the wrong person; the goal check nevertheless passed. The rehearsal that followed went through four steps: + #strong[Annotation.] An annotation of 7 August located the failure at the planning stage: no letter identifiers for #emph[Georg Hartmann], although the edition's person index lists him, and an empty roster, although Schöner and Savorgnanus demonstrably mention him. + #strong[Patch.] The loop turned this annotation, together with one on the Schöner letter-subject golden, into a new version of the planning instruction, `plan v1.1`. It adds a single rule: a query that targets a specific person, or a roster of senders and years, must carry explicit filters (sender, time window, letter identifiers) instead of falling back on an empty general filter. + #strong[Gate.] The A/B gate could not decide. The planning stage is scored by intent match, which stood at 1.0 before and after the patch; a score that cannot rise cannot show an improvement. + #strong[Promotion.] The version row records that `plan v1.1` was promoted by hand, with the gate bypassed, and both annotations remain open. The case shows rehearsal in its real proportions. Every step left a record --- a log, an annotation, a versioned patch with its predecessor kept, and the reason for its promotion --- and so a reader can check what was changed and on what grounds. It also shows that a gate is only as discriminating as the metric it reads: where the metric is saturated, promotion falls back on judgement, and the record says so. = Quoting Scores in Scholarly Print == The Display Language and Its Anatomy In musicological publications, scholars do not reprint entire orchestral scores; they cite specific measures, annotate notable passages with bar numbers, and explain their compositional significance. Scholarly papers discussing AI workflows require an equivalent display language. The `arxivai` typst package implements this standard through five coordinated elements: - #strong[Small-caps header strip.] Displays the score identifier and specific stage path (e.g., `PARTITUR: ai-query-phase1 / stanza-or-skip`), preventing quoted notation from being confused with running prose or configuration files. - #strong[Indentation-preserving code block.] Renders YAML syntax cleanly with restrained semantic key highlighting. - #strong[Circled gold line markers.] Places distinct numbered markers (①, ②, ③) alongside critical lines. - #strong[Legend pane.] Keys each numbered marker directly to a concise explanatory sentence immediately beneath the code block. - #strong[Runtime footer strip.] Names the execution engine responsible for playing the movement (e.g., `PLAYED BY Conductor / pdai:duckdb`). == Interleaving Prose and Notation In rigorous scholarly exposition, code fragments must not float disconnected from the narrative. Every score-box in this paper is introduced by argumentative prose establishing the operational requirement, followed by the quoted notation, and concluded by an analysis of the structural consequence. Numbered markers can be cited inline in running text --- for example, citing #arxivai.mark(1) to reference top-level instrumentation --- ensuring that analysis and formal specification remain tightly integrated. = Implications: The Humanist as Composer and Editor == Shifting the Division of Computational Labor Elevating orchestration into declarative Partitur scores transforms the traditional division of labor in digital humanities: - #strong[The scholar as composer.] The domain researcher formulates research questions, designs goal trees, establishes editorial iron rules, defines evaluation criteria, and curates golden benchmark datasets. - #strong[The software engineer as instrument maker.] The technical developer constructs robust runtime conductors, builds high-performance database indices, and optimizes execution backends. Scholars no longer wait for custom software features to be implemented. When a new analytical requirement arises --- such as cross-referencing Latin geographical adjectives against a regional gazetteer --- the researcher composes a new Partitur movement using existing primitives. == Reviewability, Teachability, and Accountability This architectural model reinforces three foundational standards of scholarship: - #strong[Reviewability.] Peer reviewers can examine the exact YAML score submitted with a publication, inspecting every decomposed goal, prompt constraint, and threshold. Research methodology becomes fully transparent. - #strong[Teachability.] The Partitur vocabulary is compact and intuitive. A graduate seminar can analyze and modify real agent scores without mastering complex programming languages. - #strong[Accountability.] Because every performance produces a checkable execution trace matching the score's declared movements, computational claims can be traced to the operations that produced them, and through the `fr:` addresses of the Famulus Reference to the pages and segments they rest on. = The Orchestrator Ecosystem and Companion Papers == The Integrated Scholarly Architecture The Partitur score language functions as the central orchestration layer within an integrated suite of scholarly tools: - #strong[AI Erasmus.] The research environment providing interactive operations (reading, querying, dictionary lookup, network analysis, comparison) across critical editions @grasshoff2026aierasmus\; the Famulus Handlungsmodell chapter in that paper describes how iterative goal expansion drives the famulus's action repertoire --- every subgoal binds to a registered Partitur, and the generation-Partitur loop is to extend the repertoire through the same A/B gate as instruction patches (it has not yet registered a generated score). - #strong[PDAI Document Substrate.] The page-centric XML document format and OCR pipeline that supplies structured historical texts @grasshoff2026aischolar. A new collection enters it through a score of its own, `pdai-collection-intake`: eight stages from the inventory of an import folder through an owner review gate to the export of the collection. - #strong[Famulus Reference.] The store that gives every document, page, and segment of a collection an `fr:` address, a citation derived from recorded bibliographic data, and a provenance chain, served to other tools through one MCP server. - #strong[Famulus Reasoning.] The justification layer in which an answer is the conclusion of an argument over addressed records, checked by critical questions; its scores are the ones that enforce the justification contract of Chapter 2 @grasshoff2026famulusreasoning. - #strong[Golden Queries Testbed.] The canonical thirteen-query benchmark suite and monitor logging framework that audits pipeline precision against historical ground truth @grasshoff2026goldenqueries. - #strong[Wittgensteinian Lexical Grammar.] The use-based concept dictionary framework modeling historical semantics through explicit rules of usage @grasshoff2026lwgrammar. - #strong[Famulus Notebook.] The document form in which a scholar asks a question: each block names the Partitur it plays, its scope, and its oracle, and a log beside the notebook keeps the trace the Conductor returns, the verdict, and the artefacts @grasshoff2026famulusnotebook. The Partitur provides the unifying notation that binds these systems into an inspectable research instrument. == Verified Implementation State The state of the orchestration engine and its scores in October 2026: - The Conductor runtime engine is implemented in Python under `development/ai-agent-orchestrator/`. Its agent store holds 75 scores; the Partitur register lists 91 as active, one of them withdrawn on 18 October and not yet removed. - The score loader enforces the four invariants of Chapter 2; guard cycles are bounded at run time, not excluded at load time. - The production score `ai-query-phase1` resolves the corpus queries of the AI Erasmus platform in 47 declared stages. On 20 October 2026 its routing gate reached the expected route for 13 of 14 golden queries @grasshoff2026goldenqueries. - Stage contracts are checked on every attempt and enforced by eighteen scores of the Famulus justification layer. - The Harvest learning subsystem and pipeline synthesis are implemented and opt-in. Rehearsal has so far changed instructions only: ai-query's version history records instruction versions, rejected ones included, while its table of Partitur versions is empty. - Collection intake is played from `pdai-collection-intake`\; the first collection taken through it, Georg Hartmann's #emph[Collectio figurarum] of engravings and woodcuts of sundials and astronomical instruments, passed its owner review gate on 17 October 2026. = Conclusion The barrier between the humanities and computational research was never the inherent complexity of calculation, but the opacity of imperative programming notation. The AI Partitur demonstrates that agentic workflows can be expressed in a transparent, declarative score language that humanists can read, critique, and compose. By aligning computational orchestration with the established traditions of musical scores, literate programming, and critical editing, the Partitur establishes a foundation for trustworthy, reproducible digital scholarship. = References #block[ ] #bibliography(("bibliography.bib"))