Skip to content

The history holds a take per track, and the RC-500 opens pull, rename, clear and restore - #161

Merged
AliceLafox merged 9 commits into
mainfrom
feat/rc500-history-tracks
Oct 25, 2026
Merged

AliceLafox merged 9 commits into
mainfrom
feat/rc500-history-tracks

Conversation

@AliceLafox

@AliceLafox AliceLafox commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

The history kept one take per slot state. A memory of the two-track RC-500 holds a take per track, so before anyone has a history database the model changes: a slot state is a body plus the set of takes its track folders hold, at most one per track, each knowing its track. The RC-500 then opens the operations that carry a memory whole, every track of it: pull, rename, clear and restore. Everything else stays closed on it.

What changes

  • The core (Commands.hpp). Take carries its track, SlotState is a body and a list of takes, and the Archive and the journal's audioWritten / loudnessMeasured hear the track. restore writes each take into its own track folder, archives every track's files first, and ends a track with no take empty. clear archives and removes every track. A take on a track the model does not have, two takes on one track, and a body with fewer or more track sections than the card's model are refused before anything moves.
  • The recorder. Every row carries the track the core reported. A landed take is stamped from its own track's folder. After an operation each track is written down from its own folder. A hash is inherited only from rows of the same track, because both tracks can hold a file of one name, size and stamp. A sighting stays a fact about the first track: take_sightings keeps no track, and every read that files one reads only that track. No schema change.
  • The readers. The timeline, card rows, the row model, both Restore buttons and Undo read one take per track, in track order. A row is restorable when every take of its state can be produced. What it restores is what the operation left: a clear's archived takes are no part of the empty slot it left. Until now both Restore buttons put a clear's archived take beside its empty body, a state the core refused. Undo is still hidden in 0.9.6. Its code and tests are made true for N tracks, and no Undo feature is added.
  • The profile and pull. kRc500 opens read, pull, rename, clear and restore, and a never-touched memory of it is on record now (see the next section). Pull names two-track files <slot> - <name> Track1.wav and ... Track2.wav; RC-5 names do not change. A refusal on a model the app reads in part says what is open there. The settings pair is written only where the controls are open.
  • The History tab. A row of a two-track memory names its takes by track, the way the table's WAV column does ("T1 001_1.WAV, T2 001_2.WAV"). Its menu has an export line per track, "Export track 1 take..." and "Export track 2 take...", each grey when that track's take is not kept, so the menu keeps its shape from row to row. An exported take's proposed file name carries the track. A one-track memory keeps its single "Export take..." line. Restore this state puts back every track's take.
  • The restore rule, merged with the History row menu (The History tab for 0.9.6: a row's menu, Export take, Restored, Original, UTF-8 #162). Both sides had found that a clear's archived take is no part of the state it left. There is now one rule for N tracks, restorableState, beside recordedState, which both Restore paths read. A row's state is its body and the takes it left. The row is restorable when, on every track, the body's WavStat agrees with the take beside it and that take is kept. The core's refusals use main's ASCII dash.
  • The face. The slot menu greys each closed item and ends with one sentence built from the profile table. The status line, the inspector and the Rhythm tab say what the card's model has open. A memory with only its second track recorded counts as holding audio, so it can be pulled and cleared. With no pedal chosen, Disconnect walks out the first pedal of the card's own model, never the first RC-5. Connect sends nothing when no pedal was chosen.

The RC-500's "factory" memory is derived from card copies, not from the factory

Clear needs the state of a never-touched memory, and no factory-fresh RC-500 card, and no clear done by the pedal itself, has been seen. kRc500FactorySections comes from the one RC-500 this app has met, its card copied five times as it filled up. In both banks of every copy, every memory without a take is byte for byte the default name plus that text: 98 of the 99 in the emptiest bank and 92 in the fullest. Every memory that differs holds a take. Whether the pedal's own MEMORY CLEAR leaves exactly this, and whether the pedal keeps our cleared body as written, is a step of the hardware checkpoint.

Known limit in 0.9.6

A two-track memory has no loudness number. take_sightings keeps no track (schema v10, unchanged here), and every read that files a sighting reads the first track's file, so the history can speak for track 1 only. Rather than show track 1's number where it would pass for the mix's, the loudness cell of a memory with more than one track stays empty. The history does not fill it, a double-click does not measure it, Check loudness is not offered, and the player meters one-track memories only. A per-track loudness needs a track in take_sightings and is left for a later release.

What does not change

For the RC-5, every byte the history writes, reads and restores. tests/golden/rc5-history-rows.txt was written by history_golden_tests from the code before this branch (its first commit). It pins one scripted session row for row: first sighting, push, replace, rename, trim, downmix, tempo, swap, clear, Restore this state and Undo. It matches on every commit after it.

Review

An adversarial review by a fresh session found one P1 and four P2s, all fixed in the last commit. The P1: restore removed track 1's take before track 2's was archived. The P2s: a pulled pair split across stems, track 1's loudness on a two-track row, CRLF on the golden text, and the Disconnect fallback reading the model after the unmount. A second review attacked the RC-500 factory memory and found nothing to fix in code. Running the real clear on a scratch copy of the card turned the pedal's newer bank into its own older bank, byte for byte.

Tests

  • New suites: history_golden_tests (the RC-5 rows), history_tracks_tests (the history of a two-track card, end to end through the recorder), card_permissions_tests.
  • The suites that pinned "the RC-500 is read-only" are rewritten to the new spec. Each of them still proves every closed operation is refused before a byte moves.
  • The two-track invariants were mutation-checked. Taking the track out of heldBefore fails the tests. Keeping archived takes in a restored state fails them too, with the exact refusal the old code produced.

Not in this PR

  • The History window (hidden in 0.9.6) still exports the first kept take of a row.
  • The doctor still checks the first track only.
  • macOS auto-mount keys on the volume label "BOSS RC-5". How the RC-500 card is labelled and mounted is a fact for the hardware checkpoint.

One scripted session on a synthetic one-track card goes through the
recorder as the app wires it - the first sighting, push onto an empty slot
and over a take, rename, trim, downmix, set tempo, swap, clear, Restore this
state, Undo - and every row it leaves is dumped and compared with a text
written by this suite from the code as it stands now. The history is about to
learn that a memory can hold a take per track; the RC-5 has one, so nothing
it records may move, and this is the check that says so line by line.
…lk every track

The core's take carried no track: a slot state was a body and at most one
take, the archive and the journal heard a slot and a file name, and restore
and clear looked in the RC-5's one folder. A two-track memory holds a take
per track, and the two can share a file name, so the track is now part of
every take the core names - in commands::Take, in SlotState (a body and a
list of takes), in the Archive and in the journal's audioWritten and
loudnessMeasured.

restore writes each take into its own track's folder, archives whatever
every track holds before it leaves, ends a track the state names no take for
empty, and checks each track section of the body against the take beside it.
A take on a track the model does not have, two takes on one track, and a
body with fewer or more track sections than the card's model are refused by
name before anything moves. clear archives and removes every track's files.

Push, trim, downmix and normalize stay on the first track: they are closed on
every model with more. On the RC-5 nothing changes - the same files, the same
archive calls, the same refusal words - and the golden record of what the
history writes for an RC-5 session still matches line for line.
The recorder wrote every take as track 1: what an operation archived, what
landed, and what each slot held afterwards. Now the track the core reports
goes into the row, a landed take is stamped from its own track's folder, and
after an operation each track is written down from its own folder, skipping
only the tracks the operation already wrote. Where a slot's take inherits a
hash from an earlier row, only that track's rows are asked: on a two-track
card both tracks can hold a file of one name, size and stamp, and what one
track held must never be handed to the other.

A sighting stays a fact about the first track. The reads that file one read
no other track, and take_sightings keeps no track, so the readers ask it
about track 1 alone; hashOfSighting now takes the track of the take it is
asked about and checks that track's own WavLen. The recorder learns the
card's model from its marker when the session opens.

A two-track card's first sighting already kept every track; it is now
covered: two rows for a memory with two takes, one row on track 2 for a
memory with only the second track, no empty track-1 row beside it.
…y track

The readers took one take per row: the timeline asked for a single row with
LIMIT 1 and no order by track, a card row carried one archived take, the row
model and both Restore buttons put back one take on the first track, and an
Undo step kept, restored or emptied the slot as a whole. Each now reads a
list, one take per track in track order.

- The timeline offers the state's takes, or the takes it archived when the
  operation left the slot empty, and a card row carries every take it
  archived.
- A row is restorable when every take of its state can be produced, and
  what it restores is what the operation left: a clear's archived takes are
  no part of the empty slot it left. One reader, recordedState, builds that
  state for the slot tab and for the History window alike - until now both
  put a clear's archived take beside its empty body, a state the core
  refused.
- An Undo step names the takes that go back and the tracks that go empty;
  every other track keeps its take, and the run hands the core that take
  as the card holds it, so a track the operation did not change is left as
  it was. Putting a body back checks every track section against the card.

Undo stays hidden in 0.9.6; its code and tests are made true for N tracks
and nothing is added to it. On the RC-5 every row and step reads as before,
and the golden record still matches.
…s each operation

The two-track model was read-only. It now opens the operations that carry
a memory whole, every track of it: pull, rename, clear and restore. The
rest work on the first track alone or move only its audio, and stay closed;
a refusal on a model this app reads in part says what is open there
instead of "only speaks RC-5".

- Clear needs the model's never-touched memory. The RC-500's is on record
  now, taken from a real card: the default name plus it reproduces, byte for
  byte, every factory-empty memory of two copies of that card.
- Pull names a two-track memory's files "<slot> - <name> Track1.wav" and
  "... Track2.wav"; the RC-5's names do not change. Two takes of one memory
  under one file name, pulled with the pedal's own names, no longer land on
  each other.
- The settings pair is written only where the controls are open: with a
  rename open on the RC-500, "any write is open" no longer let it through.
- The face asks the operation, not "is anything writable": the slot menu
  greys each closed item and ends with one sentence built from the table,
  the status line says what the card's model has open, the inspector and
  the drums say why a control is a lamp. A memory with only its second
  track recorded is a memory with audio: it can be pulled and cleared.
- With no pedal chosen, a Disconnect walks out the first pedal of the
  card's own model, never the first RC-5, and Connect sends nothing to
  nobody.
…k memory gets no first-track number

An adversarial review of the branch, and one of the RC-500 factory memory.

- restore archived and removed each file in one pass: on a two-track slot an
  archive that refused the second take left the first already deleted and
  the body still claiming it. It now keeps every track's take first, then
  removes, as clear always did.
- Pull disambiguated colliding names one file at a time, so the two takes of
  one memory could land as "03 - take Track1.wav" beside "take Track2.wav".
  A collision now prefixes every take of that memory.
- The loudness cell's double-click and the history's readings filled a
  two-track memory's LUFS cell with the first track's number, which the
  context menu already declines to measure. Neither does so now: the cell
  stays empty on a memory with more than one track.
- The golden text is compared byte for byte; .gitattributes keeps a Windows
  checkout from turning its line endings.
- A Disconnect without a chosen pedal reads the card's model when the eject
  is asked for, not after the unmount, when a scan may already have found no
  card; with no model it says so instead of naming an empty one.
- The factory memory's source comment counts the emptiest bank right (98)
  and says plainly it is derived from card copies, not from the factory.
- pull, rename, clear and restore on the RC-500 leave both SYSTEM banks
  byte for byte.
Restore rule, one for N tracks: a row's state is its body and the takes it
left, and it is restorable when, track by track, the body's WavStat agrees
with the take beside it and that take is kept (restorableState, beside
recordedState, which both Restore paths read). The core's refusals take
main's ASCII dash.
… track

The History tab read one take per row. On a memory of two tracks a row now
names its takes by track, the way the table's WAV column does ("T1
001_1.WAV, T2 001_2.WAV"), and its menu has an export line per track:
"Export track 1 take..." and "Export track 2 take...", each grey when that
track's take is not kept, so the menu keeps its shape from row to row. The
file proposed for a track's take carries the track, as a pulled one does. A
memory of one track keeps its one "Export take..." line and its words.

Restore this state already puts back every track's take (recordedState);
the row says so by being restorable only when each of them is kept.
@AliceLafox
AliceLafox marked this pull request as ready for review October 25, 2026 19:30
@AliceLafox
AliceLafox merged commit f769ba0 into main Oct 25, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant