cgcardona / muse public
music-query-dsl.md markdown
101 lines 3.4 KB
6d8ca4ac feat: god-tier MIDI dimension expansion + full supercharge architecture Gabriel Cardona <gabriel@tellurstori.com> 1d ago
1 # Music Query DSL
2
3 ## Overview
4
5 The music query DSL allows agents and humans to search the Muse commit history
6 for specific musical content without parsing MIDI bytes for every commit.
7
8 ```bash
9 muse music-query "note.pitch_class == 'Eb' and bar == 12"
10 muse music-query "harmony.quality == 'dim' and bar == 8"
11 muse music-query "agent_id == 'counterpoint-bot'"
12 muse music-query "note.velocity > 80 and track == 'cello.mid'"
13 ```
14
15 ## Grammar (EBNF)
16
17 ```
18 query = or_expr
19 or_expr = and_expr ( 'or' and_expr )*
20 and_expr = not_expr ( 'and' not_expr )*
21 not_expr = 'not' not_expr | atom
22 atom = '(' query ')' | comparison
23 comparison = FIELD OP VALUE
24 FIELD = <see field table below>
25 OP = '==' | '!=' | '>' | '<' | '>=' | '<='
26 VALUE = QUOTED_STRING | INTEGER | FLOAT
27 ```
28
29 ## Supported fields
30
31 | Field | Type | Description |
32 |-------|------|-------------|
33 | `note.pitch` | int | MIDI pitch (0–127) |
34 | `note.pitch_class` | str | Pitch class name ("C", "C#", …, "B") |
35 | `note.velocity` | int | MIDI velocity (0–127) |
36 | `note.channel` | int | MIDI channel (0–15) |
37 | `note.duration` | float | Duration in beats |
38 | `bar` | int | 1-indexed bar number (assumes 4/4) |
39 | `track` | str | Workspace-relative MIDI file path |
40 | `harmony.chord` | str | Detected chord name ("Cmaj", "Fdim7", …) |
41 | `harmony.quality` | str | Chord quality suffix ("maj", "min", "dim", "dim7", …) |
42 | `author` | str | Commit author string |
43 | `agent_id` | str | Agent ID from commit provenance |
44 | `model_id` | str | Model ID from commit provenance |
45 | `toolchain_id` | str | Toolchain ID from commit provenance |
46
47 **Note fields** match if *any* note in the bar satisfies the predicate —
48 i.e. `note.pitch > 60` is true for a bar if it contains at least one note
49 with MIDI pitch > 60.
50
51 ## Examples
52
53 ```bash
54 # All bars where Eb appears.
55 muse music-query "note.pitch_class == 'Eb'"
56
57 # Diminished chord in bar 8 specifically.
58 muse music-query "harmony.quality == 'dim' and bar == 8"
59
60 # High-velocity notes in the cello part authored by an agent.
61 muse music-query "note.velocity > 100 and track == 'cello.mid' and agent_id == 'melody-agent'"
62
63 # Notes outside a comfortable bass range.
64 muse music-query "note.pitch < 36 or note.pitch > 96" --track bass.mid
65
66 # Everything from a particular AI model.
67 muse music-query "model_id == 'claude-4'"
68 ```
69
70 ## Architecture
71
72 The DSL is implemented in three layers in `muse/plugins/music/_music_query.py`:
73
74 1. **Tokenizer** (`_tokenize`) — regex-based lexer producing `Token` objects.
75 2. **Recursive descent parser** (`_Parser`) — produces an AST of `EqNode`,
76 `AndNode`, `OrNode`, `NotNode`.
77 3. **Evaluator** (`evaluate_node`) — walks the AST against a `QueryContext`
78 that provides the bar's notes, chord, and commit provenance.
79
80 The top-level `run_query()` function walks the commit DAG from HEAD, loading
81 each MIDI track from the object store, grouping notes by bar, and evaluating
82 the predicate.
83
84 ## CLI flags
85
86 ```
87 muse music-query QUERY
88 --track PATH Restrict to one MIDI file
89 --from COMMIT Start commit (default: HEAD)
90 --to COMMIT Stop before this commit
91 -n N Max results (default: 100)
92 --json Machine-readable JSON output
93 ```
94
95 ## Related files
96
97 | File | Role |
98 |------|------|
99 | `muse/plugins/music/_music_query.py` | Tokenizer, parser, evaluator, `run_query` |
100 | `muse/cli/commands/music_query.py` | CLI command `muse music-query` |
101 | `tests/test_music_query.py` | Unit tests |