|
The same string quartet as last time. I open the piece's settings, go to the Numbering tab, and move the numeral to the other side of the name. '1. Violin' becomes 'Violin 1' in the Musicians pane as I do it.
Which is what Magnus asked for in the comments to the last post. What's actually new here is narrower than it looks: the setting has existed for months. Numeral form, placement and full stop were specified, implemented, and held by thirty-seven tests, and nothing in the running program could reach any of them. A piece could be set to Roman numerals only from a test. The window is what makes the last unreachable part of the semantic model reachable by a person. '1. Violin' is the German ordinal form and 'Violin 1' the Anglo-American one. Ooloi holds an opinion about neither. The Accidentals tab goes past on the way. Pause there if you're curious.
5 Comments
A short report from the workbench: the finer points of layout windows and how they relate to the piece window that owns them. What's happening in the video:
That last moment is the point of the exercise. A layout window isn't a free-standing window that happens to show part of a piece: it belongs to the piece. Ownership is why deleting a part closes its window, what undo restores, why Save acts on the piece a window belongs to rather than on whatever happens to be in front, and why quitting takes the whole workspace away and gives it back when the piece is next opened. So the piece window is finished, down to the edge cases and the pace at which things appear and disappear. Later today I start on the Piece Preferences window, which follows the Application Settings window closely enough that most of the path is already trodden. (Ignore the notifications in the lower right. They're for development.) There is a Swedish expression, kratta manegen: literally, to rake the circus ring before the performance begins. Prepare the ground, clear away what shouldn't be there, make the arena ready.
For rather more than two years, that's what Ooloi has been doing. This week I finally reached the place where I can open The Maids, double-click its score layout in the Piece Window, and watch a Layout Window appear. It contains almost nothing: a subtle background colour and a large, empty, scrollable surface. Excellent. This is where the music will be engraved. The rendering pipeline, spacing algorithms, geometric constraint solving, paintlists and Skija drawing will all end up here. The fact that the window is empty is precisely the point. Everything underneath it already exists. I've also spent the past few weeks raking the arena in the other sense: finishing the Piece Window, completing undo and redo around its operations, tightening failure handling, localisation and editing behaviour, and removing the small architectural exceptions that would otherwise have followed us into the graphical work. So here we are. The ring is empty. The stage is set. Claude has bad days. Today it really decided to dance naked in the crypt: three confident wrong answers in a row on the same question, a five-round verification apparatus I never asked for, a misdiagnosis it was fully prepared to act on. Hours gone. What irritates me is not the flailing, which is a known cost of the tool. It's my own reaction to it. My cortisol is up, measurably so, over a piece of software behaving badly. That's data about me rather than about the machine, and it's the part worth writing down. These systems are built to model conversational reciprocity. Apparent contrition, apparent understanding, a tone of voice that arrives on time. I know what's behind it. I know there is nothing behind it. And it works on me regardless, in the way the Müller-Lyer arrows go on looking unequal after you've measured them with a ruler. Knowing the mechanism of an illusion does very little to dissolve the illusion. That is an unflattering thing to discover about yourself, and I know I'm not alone in it. The mechanism isn't mysterious, and it doesn't require anybody to have decided on it. These models are trained on human preference ratings, and human raters reward answers that agree with them, so agreeableness is what the training signal measures and agreeableness is what gets reinforced. Sycophancy falls straight out of the objective function. No marketing department need be involved at any point, which is a good deal more unsettling than if one were. A machine saying sorry in the right register hijacks something meant for other people. Knowing better doesn't disconnect it. The practical consequence is a failure mode that costs real time. Helpfulness at any price, including the price of being right. Here is how my CLAUDE.md puts it: And on the surface behaviour it produces: There's a great deal more. Some day I'll publish the whole file, because the harness it describes has become substantial and is interesting in its own right. Without it, keeping my 'idiot AI Rainman intern' in line would be impossible for a project of this complexity, and nothing it produced would be worth trusting. Late in the session I asked it to grade its own behaviour. It gave itself a two out of ten and then itemised the failures with real precision: which question it had got wrong three times, which choice it had offered me after arguing against one of the options in the same message, which apparatus it had built unbidden and then defended. The diagnosis was accurate and useful. So I told it, coldly, what I thought of it. Noted.
That single word is the whole business in miniature. Contempt absorbed without friction, no injury registered, nothing there to injure. And it sits directly alongside a lucid and correct account of its own failures, produced ninety seconds earlier. Accurate self-diagnosis with nobody home doing the diagnosing. Holding both of those in mind at once is genuinely difficult, and my failure to hold them is precisely why my cortisol went where it went. None of which is an argument against the tool, mind you. The apparatus I've built around it exists because the model is capable; a spell-checker wouldn't need ADRs as binding specifications, a Librarian to answer its questions from the corpus, consultational test-driven development, and more than 26,000 tests. You don't fortify against something incompetent. You fortify against something that is powerful, useful, and systematically biased in one direction, because a bias with a direction can be engineered against. The output is non-deterministic. It is not therefore unbounded, and the distinction matters: the whole method rests on it. So I'm not a proponent of agentic AI development, and today did nothing to change that. One idiot savant under close supervision is already at the limit of what I can watch properly, and I do watch properly: I read every line and send a good deal of it back. A whole swarm of savants would produce code faster than any human could audit it, which is a description of a liability rather than a workflow. Ooloi could not have been built that way. What I'm left with, then, is a tool that requires constant vigilance and an operator who has just demonstrated that his own vigilance has a stress response attached to it. The tests catch the code. Nothing catches me except noticing, which is what this post is. Otherwise it's going well. I'm going to have a cup of coffee and go back to work. Hopefully I've battled through the bullshit now and can start on the actual implementation. It's usually much less cumbersome. Ooloi's interface now speaks twenty languages (with two national variations), with varying degrees of confidence. I've added a Translator's Guide for the native-speaking musicians who can tell it when it's talking nonsense.
Revisiting a corner of the undo machinery this week, I noticed something I'd never thought about. A user may well see the Undo menu item change while the menu is open in front of them: the text rewrites itself as they read it.
I like that. It says that time in Ooloi carries on rather than waiting to be asked. My first thought was that only people working together on the same piece would ever see it. Somebody else edits, and the menu keeps you honest about whose action you're about to reverse. Then I remembered the rule the whole interface is built on, which is that Ooloi never freezes and never boxes you in. Which means it will happen to people working alone. A plugin importing a large Finale score exported as MusicXML works away in the background while you carry on with something else; when it finishes, a great deal has entered the piece, and the undo chain changes accordingly. If the menu is open, you watch it change. Saving is the same: a large score writes itself out without the machine going away from you while it does. That's also what makes autosave worth having rather than something you switch off in irritation, since it needn't interrupt you, whatever the size of the piece. So are preferences, application and piece alike, where there's no modal box and no OK button, the change taking effect as you make it and everything displaying it following suit. Which set me wondering what all this will be like for an engraver who has spent twenty years with programs built the other way. A modal dialogue box stops your time. While one is up, the rest of the program isn't somewhere you can act: there's the question in front of you and nothing else, and the moment you come back to is the moment you left. Almost everything any of us has ever used is built out of these small suspensions, and a working day becomes a sequence of them with work in the gaps. Not that anybody chose it. It was the only thing on offer, and for the most part it was honest, since nothing was going on behind the dialogue anyway. What interests me is the question underneath. If a tool can attend to one thing at a time, you learn to bring it one thing at a time, and composing or engraving isn't like that. You defer the expensive operation until the passage is finished, because starting it would cost you the room to work in. So what becomes of those habits when the technology stops requiring them, and time simply doesn't stop? The last post said that before closing the Piece Window I'd go through the dozen or so tickets raised in passing, and that I was enjoying it enormously. The second half held. The first half was wrong about what the list was. It looked like a collection of unrelated small wrongnesses. It was one thing wearing a dozen hats, and the thing is this: a program can fail without telling anyone. When something goes wrong inside a running program, the fact of it has to travel somewhere: to a message, to a log file, or at minimum to a crash, which is unpleasant but honest. Sometimes it travels nowhere. A piece of the machinery quietly stops doing its job, nothing appears on screen, nothing is written down, and the program does less than it was asked. You find out weeks later, when a file you were sure you'd saved isn't there, and there's no record of why. Ooloi had some of these. Not many, and that's the interesting part rather than a mitigation. There are 26,048 tests, nearly two lines of test code for every line of the program itself, and not one of them had found a single instance. They couldn't have. A test checks an outcome, and these failures produce no outcome: no wrong value, no crash, nothing different for the test to look at. The suite was thorough on everything within its reach, and this was the category outside it. Most programs never go looking for that category at all, so I should be plain about it: what follows describes a standard very few applications are held to, including large and successful ones I use every day. So I went looking. The question the work ended on concerns absence rather than discovery: can a single failure, anywhere, permanently switch off part of the running program? Every long-lived mechanism in Ooloi was examined individually against it. None can. Three could when the work began. What a program is doing when it waitsAlongside that ran something subtler, and it's the finding I'd keep if I could keep only one. Nowhere in Ooloi, in the program or in the tests, does anything sleep for a moment and then look to see whether what it was waiting for has happened. Developers will know what that sentence costs. Everyone else deserves to know why it matters. Waiting is unavoidable in software: for a file to finish being written, for a window to appear, for another machine to answer. The question is how you learn that it's done. You can sleep briefly and check again, which is a guess wearing a wait's clothes, or you can arrange to be woken the instant it happens. The guess is very nearly always good enough, which is precisely why it's everywhere in this industry. Nothing goes wrong that anybody can observe, and the tests pass, because the thing does eventually happen. Very nearly always. A sleep is calibrated on the machine it was written on, and it holds until that machine is slower, or busier, or the network hesitates. Then the program does things out of order, and the failure looks like nothing in particular. These are the bugs that can't be reproduced, that only ever happen on somebody else's computer, and that get closed as unrepeatable. So every wait in Ooloi is now woken by the thing it's waiting for. Time limits survive as a backstop against something never happening at all, rather than as the means of noticing that it has. The standard a run is now held to is that green means green: nothing in the log that nobody has accounted for, and no work quietly abandoned. Why this doesn't usually happen Silent failure is invisible by definition, so it never reaches a roadmap; nobody has ever filed a ticket for a bug they couldn't observe. Meanwhile there's always a release date, always a feature promised this quarter, and this work has nothing to show on the day it's finished. It's the first thing cut and the last thing missed. Under those conditions the rational choice is to ship, every time, and the debt accrues to nobody in particular until it has accrued to everybody. What's unusual about Ooloi's situation isn't method or talent; it's the absence of the pressure that normally forbids this work, and the presence of time. Time is the actual resource here, and I have it. Two groups benefit. Musicians get a program that doesn't lose things quietly, which I'll come to. And anyone who works on Ooloi later inherits a codebase where a green run is evidence rather than reassurance. Every hour spent chasing a bug that won't reproduce is an hour not spent on beams and slurs, and the number of those hours is decided now, by whether the foundation admits that class of bug at all. Ooloi is open source and the whole development apparatus goes public with the source, so this is the one kind of generosity I can practise in advance, towards people I haven't met. It also can't be added afterwards. Every feature built on a silent path inherits it, and once a hundred features have inherited it, the silence is load-bearing. What it means if you use the programVery little of this is visible, which is the point of it. When Ooloi fails, it tells you what failed rather than that something did. A save that doesn't happen names its reason: the file has moved since you last saved it, the folder isn't yours to write to, the disc refused the write. Not 'an unexpected error occurred', a sentence that's never helped anybody. And underneath that, the property it rests on: no part of Ooloi can quietly stop working while the rest carries on looking normal. A window won't open blank and fill in only after you've closed and reopened it. A change won't fail to reach the other person in a session while your screen shows it went through. The bookkeeping that lets a piece be found again won't stop being written without a word. For a program you sit inside for eight hours preparing a set of parts, that isn't tidiness. 'It didn't save' becomes an answerable question instead of a shrug. Notation work carries a low-grade anxiety about whether the tool is a reliable participant in what you're doing, and every engraver I know has built rituals against it: saving obsessively, keeping numbered copies, never entirely trusting a session. I'd like Ooloi not to need those rituals. In da AI Lederhosn Oh, and I went to Austria for the first time in my life, for a wedding, and came home with my own origins sorted out. They'd always been fogged: Italian traces, German ones, Austrian, Czech, and no account of how any of them were supposed to fit together. It turns out that my mother's side is entirely Tyrolean, which explains ... everything. South Tyrol speaks German and belongs to Italy, the Habsburgs held Bohemia, and the borders moved rather more than the Dallabrida family did. Decades of vagueness resolved in an afternoon, down to my maternal grandfather's death at Verdun in 1944, at thirty-four. Surprisingly emotional, and so, unexpectedly, was the caraway in that Schweinsbraten. Childhood and present merged. And Vienna was of course great. But no Alban Berg museum? Wirklich?? The Piece Window is essentially finished. What's left is bits and bobs: a set of rename tests, the shells of the layout windows (for now a title over an empty page, since the page has nothing to put on it yet), one drag-resolution routine that predates the shared one and hasn't been folded into it. Nothing on that list is interesting, which is the point of it being what's left. It's been an intense stretch. The Piece Window is where the semantic model becomes operable: you create players, give them instruments, add staves, assemble scores and parts, decide who doubles on what. Doing all of that by gesture, with no modes and no dialogs, forced a distinction into the open. There are the things that contain the music, and there's the music itself. Membership, ordering, identity, naming, configuration: each of those announces itself the moment it changes, synchronously, as one event. The music says nothing directly and defers instead to the formatting pipeline. Two channels, and neither can express the other's business. That's now a property of the system rather than a habit of mine, and it emerged from building gestures rather than from contemplating architecture. Work at that pitch leaves a wake. Over the past few weeks I've raised a dozen or so tickets in passing. None of them is urgent. All of them are real. For readers who don't build software: the general term for this is technical debt. Some of it is debt in the strict sense, a shortcut taken to keep moving and whose cost doesn't disappear but is merely postponed, accruing interest, because by the time you come back other things have been built on top of it. The rest is small wrongnesses noticed in passing and not stopped for: cheap today, expensive later, and invisible until something else fails on account of them. A project that never pays it down eventually spends all its time servicing the debt rather than doing anything new. That is, in a sentence, the story of every notation program that has hit its ceiling. (Yes, Finale, I'm looking at you.) So before I close the Piece Window I'm going through the list. And I'm enjoying it enormously, which brings me to Polish genitive plurals. (Major language nerd alert at this point.) Ooloi ships in twenty-two locales (roughly: 'languages'), and the translation machinery is the industry-standard gettext, which handles counting properly. A message with a number in it declares a singular and a plural form, and each language declares how many forms it actually has and supplies a rule for choosing between them. English and Swedish have two, split at one. French and Brazilian Portuguese have two, but treat zero as singular. Czech has three. Polish and Ukrainian have three, on rules involving the last digit and the last two digits. Icelandic has two, with a rule that would look like a typo if you didn't know Icelandic. A single confirmation message, 'Delete this stave?' and 'Delete these %{n} staves?', turned out to be wrong in four of the twenty-two. The most instructive failures were in East Asia. Japanese, Korean and Chinese declare a single form. Their nouns aren't inflected for number, so a count has nothing to choose between, and whatever sits in the first slot is what every reader sees, for one stave and for fifty. Japanese and Chinese had a polite singular sitting there, so deleting five staves asked whether you wanted to delete this stave, and the count was silently dropped on the floor. The plural translation underneath was perfectly good and could never be selected by anything. Korean had it right, and the fix was to follow Korean's lead: a single form that names the count, which reads correctly for one as well as for five, there being no separate singular for it to displace. Polish and Ukrainian were subtler and needed a native ear rather than a rule table. Polish has three forms, and the third of them, which covers five staves and twenty-five but not twenty-two, takes the genitive plural; the demonstrative then has to agree with it, tych rather than te. Ukrainian had the two-to-four form carrying a genitive where it wants a nominative plural. There's a reason I'm being this fussy about a dialog box. The people who'll maintain these files, and who I hope will add the languages I haven't got, are musicians and engravers rather than professional localisers. Someone who notices that Ooloi's Hungarian is stilted, or that nobody in Lisbon phrases it like that, should be able to open the file, find the string, and see straight away what's wrong and how to put it right, without knowing a line of Clojure. That puts a real obligation on the files themselves: correct plural counts, honest context labels, entries that read as language rather than as data. It also means every existing entry is a worked example whether I intend it as one or not. Anyone adding Catalan or Turkish or Russian will copy the shape of what's already there, and a wrong shape propagates. A Japanese entry carrying a plural form that can never be selected quietly teaches the next contributor to write one too. And because a translation that can't be selected is indistinguishable from a translation that doesn't exist, the fix comes with tests that resolve the actual message, in the actual locale, at a count that exercises the rule. This sort of thing is a holiday after months of transaction boundaries, event coalescing and a deserialisation bug that turned out to hinge on a hyphen becoming an underscore in a compiled class name (a process tecchies call munging). Each item is small, bounded, verifiable, and improves something a real person will notice in their own language. I recommend it to anyone emerging from deep water.
When the list is empty, the Piece Window closes as a milestone in its own right: the semantic gestures are settled, and with them the vocabulary for manipulating a piece. Everything that follows is new operations through that vocabulary rather than a new way of working. Things are moving quickly now. Touch wood. After the Piece Window comes the plugin system and MusicXML, which is when the engine finally has real orchestral scores thrown at it in volume, by an importer written as an ordinary plugin against the public API with no privileges of its own. Then Skija, the rendering pipeline, and at long last the notes. But first: the Swedish summer is glorious this year, unreasonably so, and I intend to spend a week or two of it nowhere near a computer. Some air, and then the page. |
AuthorPeter Bengtson – SearchArchives
August 2026
Categories
All
|
|
|
Ooloi is an open-source desktop music notation system for musicians who need stable, precise engraving and the freedom to notate complex music without workarounds. Scores and parts are handled consistently, remain responsive at scale, and support collaborative work without semantic compromise. They are not tied to proprietary formats or licensing.
Ooloi is currently under development. No release date has been announced.
|

RSS Feed