Skip to content

Official Changelog


neumaRk 0.6.0 — Current version

Reference version declared by the examples in the specification (nrk:0.6). It extends 0.5.0 with a broad wave of features, listed below.

What's new in 0.6

0.6 extends 0.5.0 with the following features; each is specified in the referenced document.

Feature Document
Grace notes neumaRk_grace_notes.md
Second voice N2 neumaRk_voices.md
Voice crossing X (two voices swap position; oblique "X") neumaRk_voices.md §7
Staff continuity N+ / multi-stave neumaRk_datapack.md §4
Measure repeat % neumaRk_flow_and_repeats.md §7
Polychords [top\|bottom] neumaRk_chords.md §6
Comment-label / alternate chords C+ neumaRk_chords.md §7
Harmony persistence (empty cell + leading . continuation) + no-chord NC + silent harmonic rest R neumaRk_chords.md §1.1-1.3
Unified text markup neumaRk_text_markup.md
Collapsible sections [? …] / [!] neumaRk_markers.md §4
Extended dynamics D) neumaRk_dynamics.md
Play directive (=Style,Tempo) neumaRk_play_directive.md
PLAY) / FORM) neumaRk_play_and_form.md
Versions %%NAME … %%end neumaRk_versions.md
Articulations A) (base + extensions §8-12) neumaRk_articulations.md
Lyrics L) + LYRICS) block neumaRk_lyrics.md
Extended notes N) (noteheads, glissando, rhythm {}) neumaRk_notes_and_durations.md §11-12
Editorial group parentheses ( … ) in N) neumaRk_notes_and_durations.md §11.4
Unpitched notes = (staff position, exempt from transposition) neumaRk_notes_and_durations.md §11.5
Multi-measure rest RN neumaRk_notes_and_durations.md §13
Spacer s (invisible rest) neumaRk_notes_and_durations.md §14
Section anacrusis (>>> open measure + > mid-song upbeat) neumaRk_notes_and_durations.md §7.1
Authorial chromatic-display default chr (tonal read without a key signature) neumaRk_header.md §10.3
Collections (book / playlist) neumaRk_collections.md
Reprise pickup (>( … ) out-of-time upbeat in parentheses) neumaRk_notes_and_durations.md §7.2
Footnotes (reference [^label] + FOOT) block) neumaRk_footnotes.md
Text lines TEXT) (multi-line prose positioned in the flow: line breaks respected, empty lines, per-line inline alignment, justify with markup, vertical anchoring ^/_) neumaRk_text_line.md
Caesura (Luftpause) V — note-anchored articulation on A) (uppercase V ≠ up-bow v) neumaRk_articulations.md §3
Chromatic ornaments (mordents / turn / trill with an accidental on the auxiliary) neumaRk_articulations.md §3.1
Boxless measure text decorator \| "text"\| (no-box twin of [text]) neumaRk_flow_and_repeats.md §4.3
Frame model for all text containers: "…", ["…"] (shorthand […]), ("…") on chord labels, A), D), end-decorators. New code W177 neumaRk_text_markup.md §2.1
Note-anchored FINE (end of the piece on a note, A) line) neumaRk_flow_and_repeats.md §4.2.1
Unified begin-barline decorator: clef combinable with meter/key in (…) (\|(3/4,Ab,@F)) + composition with the anacrusis (>(@F), canonical (@F) >g8); E125 extended to any clef on N2 neumaRk_datapack.md §7.1
Measure-context object (…) at both boundaries: the barline decorator extends its vocabulary to the harmony in effect (@F13, @NC on C)) and to a pitch+duration reference (f@4_8), opens the exit position (… (@G):open\|) and free order relative to flow decorators (canonical \|[1](3/4)). A context-only row is not printed, and a datapack of only such rows takes no space. New codes E135, E136, W169 neumaRk_datapack.md §7.3
Second-level comment INFO): text for the reader, never engraved nor exported, with an optional engraved label "…"; anchored by position (header, head/tail of a datapack, standalone, version block). New codes E137, W170, W171, W172 neumaRk_datapack.md §11.1
External links in markup: [text=>url] and the short form [=>url] (shows the domain); http/https only, address literal up to the ]; activatable on screen, plain text in print. New codes W175, W176 neumaRk_text_markup.md §3quater
Persisting harmony joins the hermetic context of version blocks: every alternative starts from the harmony in force on entering the region, and after %%end an empty cell inherits the canonical block's, whichever version is selected. New code W178 neumaRk_versions.md §4.1.1
Fingering row $) (2026-10-01): pressing finger (0–5, substitution n-m), string (A–G with b/#, or 1–9), plucking finger (p i m a c), one token per event like A); <…> per chord pitch; position prefixes ' - +; fixed position [ … ] with optional label. New codes W180, W181; new subcodes W131.excess_fingering, W144.position_*, W174.fingering_orphan neumaRk_fingering.md
Content beside a short system in the Format row F) (2026-10-08): written on the side of the alignment mark where it should appear (F) \|* On cue, D.S. al @, F) Dal $ al @ *\|, F) Solo: \|*\| [B]x2); each side is an unquoted FORM) body (prose, section boxes, $ @ &fermata, ;, footnotes, links), descriptive only. The alignment mark now counts only as a whole token. New subcode W182.no_room neumaRk_datapack.md §9.1

Notable normative changes in 0.6

Beyond the new features, 0.6 introduces a few changes to syntax or codes relative to earlier drafts:

  • ; breaks the line in every text container (neumaRk_text_markup.md §3bis). It used to apply only to note annotations and FOOT) entries; it now applies to all containers, in both forms "…" and […]: M) and D) annotations, chord comment-labels, labels of A) and of PLAY)/FORM) boxes, end-decorators. Excluded, with a literal ;: the section name M) [NAME], the version label and the INFO) label. All these containers draw ; on multiple lines: the line closest to the staff stays where the single line would be and the stack grows away from the staff (upward above the staff, downward in D) and below a PLAY)/FORM) box); box labels also wrap to the box width, line by line. In the prose of TEXT) and of the INFO) body ; is literal: the INFO) body breaks lines only on the source newline, no longer on ;. In PLAY)/FORM), where a source newline counts as a space, ; breaks the line in prose too (neumaRk_play_and_form.md §6.3). \; writes a literal ; in any text (§4). No migration: no known document used ; in these containers.
  • \" no longer closes a "…" container (neumaRk_text_markup.md §4). The escape was already in the spec, but the note annotation c4"the \"King\"" (also ("…"), and ["…"] with an odd number of quotes), the chord comment-label and the end decorator stopped at the first ": the note vanished with E001, the chord row gave E102. \" and \\ now work. The header stays plain text, with " and \ literal (neumaRk_header.md §5). In the M) row a section ["NAME"] means [NAME] (neumaRk_markers.md §3.1): before, it became a section whose name kept the quotes and an annotation with the same text.
  • The version description leaves the language, absorbed by INFO). The "…" container written below %%NAME is no longer a description: that text is now written as an INFO) at the head of the block, which feeds the selector and, through its label, the score (neumaRk_versions.md §2.4.3). No migration: the earlier form was never published.
  • A single end rule for free-text blocks (PLAY), FORM), LYRICS), FOOT), TEXT), INFO)): a block ends at the first unescaped explicit line marker (neumaRk_datapack.md §13). The variants C+ / N+ / N2 (followed by a space or the end of the line) and the H…) header lines now end a block too; previously they were absorbed as text. An implicit marker row ([Intro]) does not end a block: after prose, write M) [Intro]. New line-start escape: \N) … is block text and the \ is removed when rendered; it replaces the "Limitation" of neumaRk_text_line.md §4. The blank-line criterion is unchanged (it ends PLAY)/FORM)/FOOT), not TEXT)/LYRICS)).
  • Two forms reserved by the link [text=>url] (neumaRk_text_markup.md §3quater). In PLAY)/FORM) a […] containing => is a link, not a section box; a // inside a link (escaped too, \[…=>…]) is part of the address and does not open a comment (neumaRk_datapack.md §11).
  • Comments //: quotes, header lines, position (neumaRk_datapack.md §11). A // between double quotes "…" is text and does not open a comment — just as a | between quotes is not a barline (§6.2, rule now written down) — except on sung L) rows and inside LYRICS) blocks, where " is a word character. The header lines of the free-text blocks (TEXT) …, INFO), PLAY), FORM), LYRICS), FOOT)) take a trailing comment like any other line: it used to end up in the block content. A comment is preserved where it is written when the document is rewritten: a comment line inside a block stays in the block (§13.1), and PLAY)/FORM) blocks keep their multi-line layout.
  • LYRICS) block codes renumbered to W157/W158. An earlier draft had the LYRICS) block share W152/W153 with Collections; every code now has a single meaning (Collections: W152–W156; LYRICS) block: W157 = non-existent section, W158 = pickup overflow).
  • E001 and W153 split by cause. E001 covered nine different errors (notes, grace, chord-stacks, rhythm notation): it keeps the generic case, the uninterpretable note token, and the other eight have their own code, E014–E021. W153 keeps the unknown override key in item:; a token without =value is now W179.
  • Flow signs can be authored on the markers line M). Segno $, coda @ and jumps (DC/D$/DCal@/FINE/al@) may also be written on M) (the recommended location), including a flow-only barline at the start of a section (M) |@); the effect is identical to authoring on N)/C). When M) and the notes/chords line carry divergent decorators on the same measure, the row written first wins (normally M), which heads the datapack) and the new warning W159 is emitted (neumaRk_markers.md §2.1, neumaRk_flow_and_repeats.md §2; until 2026-10 M) always won).
  • Optional-section close: [!] replaces [?] (neumaRk_markers.md §4.2). [!] is an anonymous non-optional section-open marker; it closes the preceding optional scope as a consequence of the general rule §4.1.
  • Measure repeat % with span N>1: the token appears in each of the N consecutive measures in the run (e.g. | a | b | %2 | %2 |), preserving the "one cell = one measure" invariant (neumaRk_flow_and_repeats.md §7).
  • Grace duration is optional with a default of 8. The first event of a grace block no longer requires an explicit duration: if omitted it takes 8 (eighth note); subsequent events still inherit the block's value. E009 remains only for a non-conforming explicit duration (allowed 4/8/16) (neumaRk_grace_notes.md §3.1).
  • An empty chord cell now makes the harmony persist (previously: no harmony). A C) measure with no symbols/%/NC re-realizes the previous chord on the downbeat (neumaRk_chords.md §1.1). To explicitly declare the absence of harmony, use the new NC token (§1.2, N.C. glyph). The implicit slash-notation trigger changes accordingly: it now includes persisting harmony and excludes NC (neumaRk_notes_and_durations.md §10). NC covers the whole measure as the sole content of its cell, but it can also sit next to other symbols: there it behaves like a chord (§1.2.1) — it takes its slot or the explicit duration NC(4), prints N.C. at that position and persists until a new harmony appears. The last harmonic event of the measure wins: C-7 NC | | leaves the empty cells silent, NC C-7 | | makes C-7 persist.
  • New R token (silent harmonic rest) (§1.3): twin of NC — it stops the harmony (no chord, no slashes, stops persistence) but draws no glyph. For when you need no printed symbol even though a line break falls on a persisting harmony. As the sole content of its cell it covers the whole measure; next to other tokens it covers a fraction and is a synonym of the chord rest r (§1.3.1, §4.3) — bare R draws nothing, R(…) draws a visible rest. Lossless round-trip (neumaRk_chords.md §1.3).
  • Chord-level comment-label with line extension (label-) (§8.5): a - glued to the closing delimiter ("break"- / [break]-) moves the label to the side of the chord symbol and draws a dashed line up to the next printed chord, an NC or R cell, or end of line/piece, ending in a hook. Idiom for break/drum fill/solo. Distinguished by adjacency from the sustain - (§4.1.2, isolated token): the glued form is purely annotative (not a slot). Lossless round-trip (neumaRk_chords.md §8.5).
  • A) line above C): text annotations on chords (neumaRk_articulations.md §2.1): an A) written above a C) anchors text annotations "…"/[…] to the printed chord tokens, one per column (marks ./> stay on the rhythm heads). Rendered above the chord symbol, or above the repeat/struck head !(4) when the token has no glyph. Per-token alignment ⇒ lossless round-trip; for the 2nd note of a rhythm use the explicit form A(4) !(4). A text token beyond the printed columns drops out of flow and emits W131.
  • A) line above C): dashed continuation line (neumaRk_articulations.md §2.1): an annotation with a glued - ("vamp"-) opens a dashed line above the chord band; every covered column wants its own standalone - (consumption, no W131), the line skips the continuation columns and stops at the next printed chord symbol / NC / R / end of line, with an always-present downward hook. Above-the-band twin of the CASO 2 inline extension (neumaRk_chords.md §7).
  • A) line above C): barline-anchored extension (neumaRk_articulations.md §2.1): the dashed chord line now also anchors to the measure borders — begin-bar -"vamp" (label at the left border, opens the line) and terminator -| (closes on the end-bar, hook, no text). Twin of notes §13.4–13.5. A "vamp"- last token stays inline on the symbol (the textual closing end-bar is deferred).
  • A) line above C): { } analysis staff over chords (neumaRk_articulations.md §2.1): { } brackets draw a square staff (bar + downward legs + optional label) over the chord span, twin of the staff over notes (§10.3). Intermediate columns use . to hold their position.
  • Tenuto/portato in A): from -/-! to _/_! (neumaRk_articulations.md §3). The - character is freed in A) for the upcoming dashed text-line role (uniform with D)); staccato ! and staccato-accent >! are unchanged. Sub-threshold impact (corpus census: 0 production songs, 3 dev test songs): legacy files with tenuto - in A) must be migrated to _.
  • Analysis staff A): source delimiters [ ] → { } (2026-08-20, neumaRk_articulations.md §10.3). The analysis staff now opens/closes with {/}, freeing […] on A) rows for the upcoming box role ["text"]. The glyph (square bracket above the staff) and the semantics are unchanged; the diagnostics are renamed W144.bracket_* → W144.brace_* (brace_open_overlap, brace_close_unmatched, brace_degenerate, brace_unclosed_eol).
  • A) text-line unified with D) (neumaRk_articulations.md §13). The A) line gains the free text with a dashed line that D) already had below the staff, with an identical vocabulary ("text"/["text"] note-anchored, "text"- opens the line, - continues/crosses barlines, -"text"/"text"- begin/end-bar anchoring). A) above and D) below, mirror renderings (hook always toward the staff). The box ["text"] is re-enabled in A) (square brackets freed after the analysis-staff → { } swap).
  • Terminator -| in A) and D) (neumaRk_articulations.md §13.5, neumaRk_dynamics.md §6.6). New token: a - glued to the composite border as the last token of the measure closes the line on the end-bar, with a hook and no text — filling the previously missing "close on the barline" case (idiomatic of rhythm tacet ----|). It is the prefix of the whole border (-|, -:|, -||, -|., -DCal@|…), uniform; the glued/spaced discriminator distinguishes it from the continuation - | that crosses the barline.
  • Note annotation: round frame ("…") (neumaRk_notes_and_durations.md §8). The annotation model becomes symmetric: the text is always "…" and the frame is an optional wrapper — "…" (none), ["…"] (box), ("…") (round, new; (" sentinel, previously E001). The legacy [text] form (box without quotes) is read-tolerated and normalized to ["text"]. The inline $F/$S directives, then recognized only in the plain "…" container, were removed on 2026-10-01 (entry "Fingering and strings on the $) row" below).
  • Line break ; in text containers (neumaRk_text_markup.md §3bis). Inside a container ("…"/["…"]/("…")) an unescaped ; is a line break (multi-line content on a single source line); \; = literal ; (added to the escape set). Sub-threshold behavior change: a pre-existing literal ; in an annotation is now reinterpreted as a line break — rare, recoverable with \;.
  • A single frame model for text containers (neumaRk_text_markup.md §2.1). The text sits between quotes, the frame is optional: "text" none, ["text"] box (reference form), [text] its shorthand (same result), ("text") round frame. It used to apply only to note annotations; it now applies also to chord comment-labels (chord, group and row level), A) texts (on the note, at the barline, above C)), D) texts (on the note, at the barline) and textual end-decorators. Left out: M) section and annotation, wave and bracket labels, PLAY)/FORM) box labels. Sub-threshold behavior changes, with no impact on existing sources (none used these forms outside notes): ["text"] no longer shows the quotes inside the box; ("text") used to be a degenerate slur in A) (W144) and a discarded parenthesis in C) and D); in D) a parenthesis outside ("…") (e.g. (mf), (text)) used to vanish silently and now emits W177. ["xN"]:| is equivalent to [xN]:|: in both N is the number of repeats (neumaRk_flow_and_repeats.md §6.1.1, which previously treated [xN]:| as purely informative text).
  • Slur ( without ) = two-note slur (2026-09-30, neumaRk_articulations.md §10.1). A ( not followed by a ) before the next ( of the same voice, or before the end, is legal and reaches the next element written in the source. Previously it emitted W144.slur_unclosed_eol and stretched to the end of the system; a second ( with one open emitted W144.slur_open_overlap and was ignored together with its ). Behavior change: ( ( . ) ) used to be one outer slur, it is now two slurs plus an orphan ). Next element a rest → W144.slur_unclosed_rest (new); W144.slur_open_overlap now only covers (( on the same note. Analysis staff and octave are unchanged.
  • Fingering and strings on the $) row (2026-10-01, neumaRk_fingering.md, neumaRk_notes_and_durations.md §8). The inline $F (fingering) and $S (string number) directives of the note annotation (c4"$F1", d"$F3$S2") are removed: fingers, strings, plucking fingers and fixed positions are written on the new $) row. The note annotation has a single slot and it is always text: "$F1" is now annotation text, with no diagnostics. Behavior change; migration: c4"$F1$S2" → $) 12 above N) c4.

Rules clarified and corrections of October 2026

A full review of the specification against the parser (2026-10) decided, case by case, which of the two to correct. The resulting rules:

  • Shorthands repeat the notehead too (neumaRk_notes_and_durations.md §5, §11.2). !, a bare duration, '/, and a bare ^ take the notehead of the previous note along with its pitch (and =, §11.5), across a rest; a head written on the token wins, an explicit pitch starts from the normal head. Behavior change: c8(x) ! gave a normal head on the second note, it now gives a cross (no song in the libraries used this form, checked 2026-10-08).
  • Duration context: the last written event (neumaRk_notes_and_durations.md §3.3). A note without a duration inherits that of the last written event, note or rest; the rests added by autofill to complete a measure do not count. Behavior change: | c4 d | e f | gave e2 f2 (it inherited the completion rest), it now gives e4 f4; a2 r8 | d e gave d4. e8^e4, it now gives two eighth notes.
  • Opening upbeat without durations (§7): > g a b | c1 at the start of the piece lost the upbeat notes; they are now a quarter note each, with the new W005. An upbeat with inherited durations that overflows the measure is a real excess (E005). The upbeat may start with a rest or a spacer (> r8 g a, > s8 g a). An anacrusis > in the middle of a row emits the new W184.
  • !, ' and , after a rest at the start of the piece (§1.1, §5): they repeat the rest (or the spacer) with the new W012, instead of an invented note. With a note before, ! keeps repeating the last note.
  • Header field by field (neumaRk_header.md §3.2). The "all-or-nothing" criterion is retired: an invalid field is discarded with a diagnostic and the rest of the header holds. New codes E303 (invalid header meter), W304 (malformed or out-of-range BPM or year, subcodes bpm_invalid/bpm_range/year_invalid/year_range), W305 (repeated field), W306 (unrecognized token on the metadata line), W307 (malformed nrk: line); E301 now actually fires. Values such as HB) 1000 or HB) 120.5 are no longer truncated. The rule "the implicit style starts with a lowercase letter" is removed: the parser never applied it.
  • nrk: is a rule of the exchanged file (neumaRk_header.md §2): mandatory in the .nrk file and in every song block of a collection; a song that reaches the parser without nrk: is valid. In a collection, a song without nrk: is merged into the previous one with the new W183.
  • Undeclared key and meter mean C and 4/4 in the parser too, and the rewritten document writes HK) and HM) only if they were declared (or if they differ from the default).
  • Allowed meters (neumaRk_header.md §10.4): denominator 2, 4, 8 or 16, numerator from 1 up to two digits, or a sum […] of parts from 2 to 5. An invalid meter in the context object (…) gives E136 (E136.context_meter_invalid) and the previous meter stays.
  • Conflicts between rows of the same measure (neumaRk_datapack.md §7.3): for meter, key and flow decorators the row written first wins, and the losing row gets W159 (context_conflict, context_key_conflict, flow_decorator_conflict). For flow decorators M) used to always win; for meter and key the last row won, silently.
  • Diagnostics promised and now emitted: W133 (text container not closed within the measure, on N)/A)/D)/M)), W004 on a grace block glued to a rest or a spacer (W004.rest_main), W008 when another stave does not carry the same RN (W008.other_stave; the degradation is one measure rest, not N), E126 on the second label of a group (E126.label_dup_group), W155 for name:/desc: after the first song or between item: and its block, W182 for an F) row that is not last, duplicated or with extra content (since 2026-10-08 also no_room, see the Format row below). W132 stays reserved, like W130.
  • Grace: a block glued on the left (c4[d8/^]e4) is a grace on the note that follows (before, two notes were lost with E001); an invalid grace event is discarded with E004 (grace_pitch_invalid, grace_force_accidental) or E009 (neumaRk_grace_notes.md §2.1).
  • Withdrawn from the specification: the implicit Format row (|*| without F)); E123 for rows interposed between N) and N2 (E123 stays only without a parent stave); the requirement of equal measure counts across staves and voices (the shorter one is completed with rests); the lone . as a final barline (. is never a barline).
  • Constructs allowed and clarified: >( … ) with detached parentheses is the same as the glued form; the leading > of D) under an upbeat is the placeholder, not a decrescendo, and an A)/D)/$) row without the placeholder starts from the first upbeat note; \] is a literal ] in the abbreviated box […], in an M) section name […] and in the NAME of PLAY)/FORM) boxes; ["a|b"] on the chord row is a label; a label at the head of a measure with no chord after it stays in its measure, on the empty cell (on C+ too), and two labels at the head of the same measure give E126; the octave nudge is written before @ (c'@4_4); the chord tie is written inside the parentheses (C(2^)); a row of % only is a chord row (the margin with a page break is -%); a leading ^ on a note after a rest is an entry tie; % also works on voice 2; _ inside an L)/LYRICS) syllable is an elision (neumaRk_lyrics.md §5); in a collection block, blank lines after nrk: and // comments in the header do not change the song title (neumaRk_collections.md §5).
  • The first barline (neumaRk_datapack.md §6, §7.3). It is optional when only the row identifier and context objects come before it: the group before the first barline counts as glued to the entry barline, on every measure row, and does not create a measure. A label, a note, a chord symbol or a text do create the measure (C) [Intro] | Eb7 F | has two measures). Behavior change: C) (Bb) | Eb7 F | gave an empty measure with the optional chord Bb, it is now one measure in B-flat; on C) and on the M)/A)/D)/L)/$) rows a meter in that position gave E102 or was lost. The label-and-barline form of neumaRk_chords.md §8.1 is one more measure, not a spelling equivalent to the form without barlines.
  • Key or optional chord on C) (neumaRk_chords.md §5): (Bb) glued to the barline or before the first barline is the key; detached after the barline or inside the measure it is an optional chord.
  • Context in free position (neumaRk_datapack.md §7.3): a detached meter at a measure boundary also holds on C) and on the M)/A)/D)/L)/$) rows, before a label as an entry, after it as an exit (it used to give E102 or be lost silently). On the M)/A)/D)/L)/$) rows a detached key or pitch reference is not context. (X) as a context key means no key signature, like (C).
  • Clef and pitch reference belong to the stave (neumaRk_datapack.md §7.3): on M)/A)/D)/L)/$) they give E135 (E135.clef_on_secondary_row, E135.pitch_ref_on_secondary_row) and are discarded; the pitch reference gives the same E135 on C) and C+ too. Behavior change: they used to change the clef, pitch and duration of the N) row silently (on C), with the chord row below N), reading did not terminate).
  • Irregular groups and unknown durations (neumaRk_notes_and_durations.md §3.2.1, §3.2.2, §3.3): a group with a different ratio closes the open one with W011; a group open at the barline whose unit does not fit in the space left is completed with rests of the same ratio and a smaller value (W002); unknown durations that cannot share the measure in writable values take a common binary value with the new subcode W005.uneven_split. These cases used to give internal errors (E90x) or inconsistent durations.
  • Non-integer BPM (neumaRk_play_directive.md §2.3.1): (=120.5bpm), (=120,5bpm) and, after a Style, (=Swing, 92,5bpm) give W136 (W136.bpm_not_integer); they used to be ignored silently, and 92,50bpm set 50 bpm.
  • New diagnostics for values ignored silently: W308 (unknown header marker, neumaRk_header.md §6.1: the line used to end up in the style), W185 (non-integer transpose= in item:, neumaRk_collections.md §6.1), E004 on a rest, spacer or non-pitch inside a chord-stack (E004.stack_rest, E004.stack_pitch_invalid, neumaRk_notes_and_durations.md §2.6: they used to become a duplicate of the first pitch), W186 (token outside the M) row vocabulary, e.g. a detached key M) | (Bb) | or free text M) | foo |, neumaRk_markers.md §7: it used to disappear; the same W186 for the second section or annotation in the same measure, M) | [A] [B] |, M) | "a" "b" |, W186.section_extra, W186.annotation_extra, §3.2: the first counts). A […] inside an M) annotation is text: M) | "a [b] c" | used to open the section b as well.
  • Trailing hyphen in syllables (neumaRk_lyrics.md §5, §7): pa- says the word goes on with the next syllable, also across the barline and on a new line; the continuation used to be lost at the barline. The rewritten document keeps the hyphen.
  • Collection block headers (neumaRk_collections.md §5): HC) carries the credits, and no H…) marker becomes the song's style.
  • Severity: an E### error does not stop reading (§ Convention below). E007 (slash with less than one beat available) no longer leaves a zero-length rest.
  • Reading corrections, with no effect on the syntax: irregular groups with an explicit ratio (c8t5:3, c8t4:3, c4t2:3), eighth-note meters beyond 15/8 (16/8), R1 read as a measure rest, final barline |., meters with a large numerator (66/2, 99/4) completed with no internal errors or lost rests, measure rest in meters of two whole notes or more (4/2, 8/4, 16/8, 32/16).
  • Corrections of the rewritten document (round-trip), with no effect on the syntax: version blocks with the same label, editorial parentheses ( … ) also inside >( … ), compound meter [3+3+2]/8 (it used to become 8/8), terminator -|., second flag on the TEXT) header (rewritten escaped), grace glued to a rest, entry tie after a rest or in the second ending, rests merged across the middle of the measure (also in chord rhythm), inline clef after a datapack of rests only, ^ mid-measure in the compact form, final barline .| not repeated in the next datapack, padding of header fields, distinct written rests that each fill a whole block of the meter (r1 r1 in 4/2: no longer merged), trailing hyphen of syllables, row label of a C) with no chord symbols (C) [Intro] |, C) "solos": the row used to disappear).
  • Reference rendering (informative): a voice silent for the whole bar is drawn as a centred whole rest, in every meter and per voice (neumaRk_notes_and_durations.md §3.3.1); a single note filling the bar is centred; the score uses a single ink, except links (neumaRk_specification.md §2). In the MusicXML and LilyPond exports the syllable elision _ is an elision and the whole-bar rest a measure rest.

Diagnostic-code convention

  • E### — error: invalid construct. The parser repairs or discards it, as described by the document that covers it, and goes on; the measure involved may be marked invalid (LOG_ERROR).
  • W### — warning: construct allowed but probably unintended, or an ignored value; parsing goes on (LOG_WARNING).

No diagnostic, error or warning, stops the reading of the document. The prefix indicates the severity, not the category. The entire 0.6+ diagnostic family (W130–W167: dynamics, articulations, play directive, PLAY) / FORM), collapsible sections, versions, HV), collections, LYRICS) block) is emitted as a warning and has been renamed from E13x/E14x/E15x to W13x/W14x/W15x to align the prefix with the actual severity.

Families. The number after the letter indicates the area, in blocks:

Range Area
E001–E022, W002–W013 Notes, durations, grace
E101–E137, W103–W186 Chords, datapack, voices, measure context and all the 0.6+ families (dynamics, articulations, flow, markers, versions, collections, lyrics, footnotes, links, fingering)
E300–E303, W301–W308 Flow and repeats (E300, E302), header (E301, E303, W301–W308)
W400 Chord rhythm
E901–E906, W999 Internal (see below)

Missing numbers inside a range are not codes: they were never assigned, except the ones marked retired or reserved in the tables, which must not be reassigned.

The two tables below — base codes (pre-0.6 and fundamental) and 0.6+ codes — together form the complete, authoritative map of the normative codes (code → meaning), aligned with the parser. Each code has a single meaning. Some codes have subcodes (W131.excess_syllable, E136.context_meter_invalid, …): they specify the cause, the meaning of the code stays the one in the table.

Internal codes (dev-facing, not normative) are deliberately excluded from these tables: E901–E906 — guards against impossible states (non-positive duration, undeducible note values, navigation with an invalid line/measure id, undetectable tuplet, non-representable note value) — and W999, a synthetic log-truncation marker emitted when diagnostics exceed the buffer capacity. Also outside the language vocabulary are E910/W910, which do not come from the parser but from an application's renderer (a measure or a rendering step that failed). Like W130, W132 and W161, they are not part of the language vocabulary. W001 is retired (never emitted).

Base code allocation (pre-0.6 and fundamental)

Code Meaning Family (doc)
E001 Uninterpretable note token (no more specific code applies; e.g. a chord-stack glued to the next note <c e g>e4, %3) Notes & durations
E002 A duration cannot follow . or ! Notes & durations
E003 Invalid tuplet Notes & durations
E004 Cannot parse pitch · grace event that is not a pitch (E004.grace_pitch_invalid) or with the force-accidental ! (E004.grace_force_accidental): event discarded · rest or spacer (E004.stack_rest) or element that is not a pitch (E004.stack_pitch_invalid) inside a chord-stack <…>, grace ones included: element discarded Notes & durations · Grace notes
E005 Note durations exceed the measure meter (also in the upbeat) Notes & durations
E006 More than one slash-rhythm / per measure Notes & durations
E007 Slash / has no room: less than one beat available after beat alignment Notes & durations
E008 Absolute octave @<n>_ requires an explicit duration Notes & durations
E009 Explicit grace duration non-conforming (only 4/8/16 allowed), or disallowed elements after the pitch (?, t, ., "…", (…)); omitting it on the first event is legal (default 8) (E009.grace_duration_invalid) Grace notes
E010 Modifier / or ^ allowed only on the block's last grace event Grace notes
E011 Empty grace block [] Grace notes
E012 Grace block with more than 4 events Grace notes
E013 Rest/spacer not allowed as a grace event Grace notes
E014 Rhythm-notation group {…} not closed Notes & durations
E015 Grace block […] not closed Grace notes
E016 Malformed chord-stack: < with no closing >, or <> Notes & durations
E017 Chord-stack with no pitch (< >) Notes & durations
E018 Prolongation/repeat/octave mark with no preceding note Notes & durations
E019 Grace event without pitch/duration Grace notes
E020 Grace chord-stack not closed (<…) Grace notes
E021 Empty grace chord-stack <> Grace notes
E022 Unpitched marker = misplaced: alone (unpitched_bare), on a rest or spacer (unpitched_rest), inside a chord-stack (unpitched_inside_stack), before a non-note token (unpitched_misplaced) Notes & durations (§11.5)
E101 Compact rhythm list / polychord / chord expression unparsable (compact_rhythm_invalid, compact_rhythm_first_rest, polychord_unparsable, chord_unparsable) Chords
E102 Unrecognized token in the chord line (also an unclosed container, a ^ outside the parentheses) Chords
E110 Too many staves in datapack (max 4) Datapack / multi-stave
~~E111~~ ~~Orphan D)/L)~~ — retired before implementation: the orphan row is tolerated (see W130). Code not reassigned —
E122 N) without match: more continuations than staves in the previous datapack (use N+) Datapack (N+)
E123 N2 without a parent stave: no N) or N+ before it in the datapack Voices
E124 Duplicate N2 on the same stave Voices
E125 Clef directive not allowed on an N2 row Voices
E126 More than one comment-label on the same chord / group / row (label_dup_chord, label_dup_group, label_dup_row) Chords (alternate)
E127 Max 2 alternative C+ rows per datapack Chords (alternate)
~~E128~~ ~~NC mixed with other content in the same measure~~ — retired: NC next to other symbols is the fractional form of the no-chord declaration (neumaRk_chords.md §1.2.1). Code not reassigned Chords
E129 More than one base C) line (chord block) in the datapack Chords / datapack
E130 Malformed voice/stave marker N2) / N+) (use N2 / N+) Voices
E131 Two X crossing markers on the same column of the staff Voices (crossing)
E132 X crossing marker without a pivot note Voices (crossing)
~~E133~~ ~~R (harmonic rest) mixed with other content in the same measure~~ — retired: R next to other tokens is the fractional form of the harmonic rest (neumaRk_chords.md §1.3.1). Code not reassigned Chords
E134 Counted note after the reprise-pickup group >( … ) (must be trailing) (E134.reprise_note_after_group) Notes & durations
E135 Clef on a chord row (C) \|(@F8vb), E135.clef_on_chord_row): on C) the @ declares the harmony in effect, and (@F)/(@G)/(@C) are chord symbols there · clef (E135.clef_on_secondary_row) or pitch reference (E135.pitch_ref_on_secondary_row) on M)/A)/D)/L)/$), pitch reference also on C)/C+: they belong to the stave and are written on N). Component discarded Measure context
E136 Unrecognized measure-context (…) component (context_component_invalid), invalid meter (context_meter_invalid: the previous meter stays), or group away from a measure boundary (context_not_at_boundary) Measure context
E137 INFO) between the musical rows of a datapack: it goes before the first row or after the last (E137.info_inside_datapack) Datapack / INFO)
E300 Measure-repeat (% / RN) with no valid source (E300.mmr_no_source) Flow / MMR
E301 Cannot parse the header key (HK) H): field discarded, the default applies Header / key
E302 Repeat count x0 (contradictory: "execute 0 times" → the material is never performed); on :x0\|/:0x\|, [x0]:\| and PLAY/FORM box […]x0. x1/x100 stay W142 (E302.repeat_count_zero) Flow / repeats
E303 Invalid header meter (HM) 5/3): field discarded, the starting meter stays Header
W002 Tuplet not closed before measure end (auto-filled with tuplet-unit rests) Notes & durations
W003 Grace block not adjacent to the main note (ignored) Grace notes
W004 Grace block without an adjacent main note, or followed by a rest or a spacer (W004.rest_main): ignored Grace notes
W005 Implicit duration with no duration to inherit: quarter notes in the opening upbeat; in a measure, unknown durations that cannot share the space in writable values (W005.uneven_split): the longest binary value that fits for all, at most a quarter note Notes & durations
W006 Pitch outside the MIDI range [1..127] Notes & durations
W007 Invalid rhythm-notation token {…}: no duration (W007.rhythm_token_no_duration), invalid duration (W007.rhythm_duration_invalid) or invalid tuplet (W007.rhythm_tuplet_invalid) Notes & durations
W008 Multi-measure rest RN over a span with content, or not shared by the other staves (W008.other_stave): degraded to one measure rest Notes & durations (MMR)
W009 Modifier not allowed on spacer s (ignored) Notes & durations (spacer)
W010.* Editorial group-parentheses span ( … ) malformed: paren_open_overlap (nesting), paren_close_unmatched (orphan )), paren_degenerate (empty ( )), paren_unclosed_eol (unclosed at end-of-line → implicit close) Notes & durations (§11.4)
W011 Tuplet group closed before completing its cycle, by an explicit duration or by a group with a different ratio (completed with tuplet-unit rests; e.g. a8t b4., c16t5 c4t7) Notes and durations
W012 ! after a rest with no preceding note to repeat: the rest or spacer is repeated · same case for '/, (W012.octave_mark) Notes & durations
W013 Unpitched note =: accidental ignored (accidental), tie with a pitched note (tie_mixed), = on rhythm notation {…} (rhythm_redundant) Notes & durations (§11.5)
W103 Unrecognized chord suffix Chords
W301 Could not deduce title or credits from the first header line Header
W304 Malformed or out-of-range header BPM or year (bpm_invalid, bpm_range, year_invalid, year_range): field ignored Header
W305 Repeated header field: the first one holds Header
W306 Unrecognized token on the implicit metadata line: ignored Header
W307 Malformed nrk: line (not nrk:<major>.<minor>): ignored Header
W308 Unknown header marker (HZ) foo): line ignored, stays header Header
W400 Chord-rhythm note with non-positive duration (omitted) Chords (rhythm)

0.6+ code allocation (single source)

The $)/A)/D)/L) lines share W131 (excess tokens) by semantic identity, not by collision.

Code Meaning Family (doc)
W130 (reserved — not emitted: an orphan D)/L) is tolerated, silently bound to stave 0) Dynamics
W131 Tokens in excess of the events of the row below: syllables (excess_syllable), articulations (excess_articulation), dynamics (excess_dynamic), fingering (excess_fingering), LYRICS) syllables on the notes of a section (excess_section_lyric); on an A) above C), texts beyond the printed chord columns $)/A)/D)/L)/LYRICS) (shared)
W132 (reserved — not emitted: a D) - not reducible to an extension/barline has no effect) Dynamics
W133 Text container opened and not closed within the measure: the rest of the measure is read as text (N), A), D), M); on C) it is E102) Text markup
W134 Empty play directive (=) Play directive
W135 Reversed slot order (tempo before style) Play directive
W136 BPM out of range (10–999) or not an integer, with a decimal point or comma (W136.bpm_not_integer), also after a Style: the directive does not set the tempo Play directive
W137 Invalid NRK figure in metric modulation Play directive
W138 Play directive in a line other than M) Play directive
W139 A) token outside the vocabulary Articulations
W140 More than one FORM) section PLAY/FORM
W141 Invalid \|BARS duration (1–999) PLAY/FORM
W142 Repeat count xN out of range (2–99), on PLAY)/FORM) boxes ([…]xN) and on the barline (:xN\|, W142.repeat_out_of_range) PLAY/FORM · Flow / repeats
W143 Keyword &xxx not reserved PLAY/FORM
W144 Malformed A) span: slur (slur_*), analysis bracket (brace_*), ottava (octave_*) — opening and closing on the same note, closing without opening, second opening while one is open, unclosed at end of row, slur arriving on a rest (slur_unclosed_rest) · malformed $) fixed position [ ]: position_degenerate, position_close_unmatched, position_open_overlap, position_unclosed_eol Articulations · Fingering
W145 FORM) not at the end of the document PLAY/FORM
W146 Redundant [!]: no optional section to close Markers
W147 %%NAME block not closed: everything to the end of the file is treated as part of it Versions
W148 Orphan %%end (no open block) Versions
W149 (retired: a new %%NAME closes the open block implicitly — do not reassign) —
W150 Malformed HV): missing NAME (hv_expected_name), list expected (hv_expected_list) or unclosed (hv_list_unclosed), trailing content after the list (hv_expected_default_or_eol), default= without a label (hv_default_no_label), or the old form HV) versions: […] (hv_legacy_form) Versions
W151 Collapsible section not at start of line (§4.7): opening (W151.optional_open) or bounds (W151.optional_bounds) · default used as a version NAME or label Markers · Versions
W152 item: line in an nrk-book: collection Collections
W153 Unknown override key in item: Collections
W154 item: line not followed by a song block Collections
W155 Collection header directive after the first block, or between item: and its block: ignored Collections
W156 Collection with no song blocks Collections
W157 [NAME] in the body of LYRICS) references a non-existent section (reference-broken) (W157.section_not_found) Lyrics (LYRICS) block)
W158 LYRICS) pickup group <…> with no notes to bind to: empty preceding run (pickup_no_prenotes) or shorter than the group (pickup_overflow) Lyrics (LYRICS) block)
W159 Meter (context_conflict), key (context_key_conflict) or flow decorators (flow_decorator_conflict) differing between rows of the same measure: the row written first wins Measure context / flow
W160 More than one reprise-pickup group >( … ) in the same measure (W160.reprise_multiple_groups) Notes & durations
W161 (reserved/deferred — reprise pickup, validation in parse_text) Notes & durations
W162 FOOT) entry not starting with a [^…] label (ignored) (W162.footnote_entry_no_label) Footnotes
W163 Duplicate explicit label in FOOT) (W163.footnote_label_duplicate) Footnotes
W164 Reference [^…] with no matching FOOT) definition (W164.footnote_ref_no_def) Footnotes
W165 FOOT) definition never referenced (W165.footnote_def_unreferenced) Footnotes
W166 Mix of explicit and auto [^] labels in the same document (W166.footnote_label_mix) Footnotes
W167 FINE on a barline and note-anchored FINE conflicting in the same measure (W167.fine_barline_note_conflict) Flow / MMR
W168 N+ redeclared: a stave of the previous datapack is left without an N) continuation Datapack / staves
W169 Context announced twice at the same point (end of a measure and start of the next) (W169.context_double_announce) Measure context
W170 Second INFO) on the same anchor: the first one wins (W170.info_duplicate) Datapack / INFO)
W171 Empty INFO) (no label, no body): ignored (W171.info_empty) Datapack / INFO)
W172 INFO) label "…" not closed on the same line: the line is read as body (W172.info_label_unclosed) Datapack / INFO)
W173 (retired before implementation — do not reassign) —
W174 A) row with no N) (nor C)/C+) directly below: ignored · $) row with no N) below (W174.fingering_orphan): ignored Datapack / articulations · fingering
W175 Link whose address is not http/https or is malformed: the text stays, not activatable (W175.link_bad_url) Text markup (links)
W176 Empty link [text=>] / [=>]: the text stays, if any (W176.link_empty) Text markup (links)
W177 Parenthesis in D) outside a ("…") frame: ignored Dynamics / text markup
W178 The blocks of a version end on different harmonies and the measure after %%end has an empty cell: it inherits the canonical block's Versions / harmony
W179 Token without =value in item: Collections
W180 Malformed $) token: the whole token is ignored Fingering
W181 $) token that cannot apply: on a rest (fingering_rest), on a prolongation . (fingering_prolongation), <…> group on a single note (fingering_not_chord) or with more entries than pitches (fingering_chord_excess) Fingering
W182 F) row not last (not_last: moved to the end), duplicated (duplicate: the last one holds), without an alignment mark written as a whole token or with a second one (extra_content: the row, or everything from the second mark on, is ignored), or with side content where the system leaves no room (no_room: content on the system's side of the mark, or with \|**\|; ignored) Datapack / Format
W183 In a collection, song block without its nrk: line: merged into the previous song Collections
W184 Upbeat mark > in the middle of a row: rendered as an empty measure followed by the upbeat Notes & durations
W185 item: transpose= that is not a signed integer (transpose=abc): override ignored Collections
W186 Token outside the M) row vocabulary (section, annotation, play directive, context at a measure boundary): discarded; W186.key_free_position for a detached key (M) \| (Bb) \|); W186.section_extra / W186.annotation_extra for the second section or annotation in the same measure (the first counts) Markers

Historical note: W144 had also been drafted for "nested PLAY)", a case structurally prevented by the parser (never emitted); the code therefore remains with the articulations. The collapsible-section-not-at-start-of-line case was moved from W147 (which collided with Versions) to W151 (2026-05-26).


neumaRk 0.5.0 — First public release

Status

Public preview. The language is usable and coherent, but subject to short-term evolution.

Version meaning

This is the first public release of neumaRk. The number 0.5 reflects an already advanced conceptual maturity, while not guaranteeing definitive stability of the semantic interface.

Included features

  • datapack-based structure
  • header with conservative deduction
  • notes with implicit and explicit durations
  • chords with rhythmic semantics and implicit/explicit durations
  • chords with a compact duration list X(a,b,r16,c,...) (see §4.4)
  • repeat and flow management
  • support for Compact / Human / Verbose formats

Normative clarifications

  • persistent musical context
  • contextual semantics of symbols
  • priority of a single note in the measure
  • deducible Format line (withdrawn in 0.6: the Format row is always declared with F))

Not yet stabilized

Historical list as of 0.5.0; many of these items were introduced in 0.6 (see What's new in 0.6).

  • articulations, dynamics, lyrics
  • polychords (syntax [top|bottom], see neumaRk_chords.md §6)
  • multi-stave per datapack (up to 4 staves, see neumaRk_datapack.md §4)
  • measure repeat (syntax % / %! / %2 / %!2 / %4 / %!4, see neumaRk_flow_and_repeats.md §7)
  • possible syntactic extensions
  • refinement of the deduction rules
  • complete formal validation

Compatibility

There are no previous public versions.