From f7d020946522d37e5a13c339dfa2f73accd18b79 Mon Sep 17 00:00:00 2001 From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com> Date: Tue, 13 Oct 2026 21:52:19 +0000 Subject: [PATCH 1/5] docs(studio): describe character ownership and recovery --- projects/MusicalInstruments/README.md | 5 + .../MusicalInstruments/studio-ownership.md | 164 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 projects/MusicalInstruments/studio-ownership.md diff --git a/projects/MusicalInstruments/README.md b/projects/MusicalInstruments/README.md index 28e1cf5..9761efd 100644 --- a/projects/MusicalInstruments/README.md +++ b/projects/MusicalInstruments/README.md @@ -22,3 +22,8 @@ no other plugin has cancelled the slot change. Maven consumers declare `net.tfminecraft.musicalinstruments:musicalinstruments` with `provided` scope and use the shared installer. + +## Recording studio ownership + +See [character projects and recovery](studio-ownership.md) for library isolation, +legacy migration, estates, compatibility, and offline rollback. diff --git a/projects/MusicalInstruments/studio-ownership.md b/projects/MusicalInstruments/studio-ownership.md new file mode 100644 index 0000000..2a4980e --- /dev/null +++ b/projects/MusicalInstruments/studio-ownership.md @@ -0,0 +1,164 @@ +# Character projects and recovery + +[Project index](README.md) · [Source](https://github.com/TF-Minecraft/MusicalInstruments) + +The recording studio requires an active character. Each character has its own +project library, selected project, pending takes and editing authority. Switching +characters or leaving finishes the outgoing take using its captured character, +stops private previews and closes studio menus. Delayed menu/dialog responses +check the initiating session; a response from an earlier session cannot edit the +incoming character's project or consume its disc. + +The original song author and author account remain on the project. Inheriting a +project grants editing authority without changing that attribution. Published +editions, their content IDs and existing disc metadata remain unchanged, so old +discs keep playing. Ordinary instruments and jukebox playback remain available +without a character provider; creating, editing and recording projects fail +closed when that provider is unavailable. + +Publication timestamps in `studio/editions/.log` and the short +publication cooldown remain account-wide. Switching characters or inheriting +another author's project does not reset them. An inherited library may exceed +36 projects; every inherited project remains available, and creating another +project waits until the character has fewer than 36. + +## Requirements and storage + +Build with Java 21 for Paper 1.21.10 and the pinned TLibs 2.6.0 API. The build and +release workflows prepare this dependency through the shared plugin installer. +They also install the actual TLibs 2.3.0 release as an isolated compatibility test +fixture; it is not packaged into the runtime jar. +TLibs/RPCharacters are optional load-order dependencies: capability probes isolate +new calls from older installations. Without the estate API, holdings stay with +their character until that integration becomes available. + +Paths below are relative to `plugins/MusicalInstruments/`: + +- `studio/projects//` contains retained legacy source projects, + their selected pointer and optional library cache. The new runtime does not + edit or use this account namespace. +- `studio/character-projects//.yml` contains editable + projects with separate `owner-account` and `owner-character` authority fields. + `selected` holds the chosen project ID and `library.index` is a rebuildable cache. +- `studio/songs/.yml` contains immutable published recordings. +- `studio/ownership/migration/.xml` records the reviewed recipient, + source/target hashes, original payloads, completion and held records. +- `studio/ownership/estates/.xml` records exact estate decisions, + full source/recipient references, expected payloads and cumulative move receipts. +- `studio/ownership/backups/.yml` contains immutable original documents. + +Unknown project properties—including nested maps and explicit null values—survive +migration and later saves. Extension metadata from a removed track or discarded +pending take stays in retained metadata without becoming playable music. +Unreadable files remain available for staff recovery; absence from the playable +library does not mean the file was deleted. + +## Legacy migration + +Migration runs after stores are ready, on ownership operations and at the hourly +reconciliation. It waits until RPCharacters reports reviewed legacy heirs ready. +Each account's legacy projects go to exactly its reviewed heir, even if that heir +is dead. The character's estate then determines any subsequent inheritance. +There is no active-character, last-played or username fallback. + +A journal inventories the account library and selected pointer before publishing +its character copies. Immutable backups, checked writes and stable IDs allow +interrupted runs to resume the same planned recipient. Original account files +remain retained and inert. A recipient with a selected project keeps that +selection; migration supplies the legacy selection only when none is present. +Unsupported, corrupt or identity-mismatched records are held. A conflicting +recipient project is not overwritten. + +Inspect journals and unassigned legacy paths from the source checkout: + +```sh +python3 tools/studio-ownership.py \ + --studio /path/to/plugins/MusicalInstruments/studio --kind migration +``` + +The JSON report lists held files, publication states, conflicts, character +projects created after the plan, and legacy paths with no completed mapping. +Do not edit a source archive to choose an heir; fix the reviewed mapping before +its first plan, or recover a previously planned transfer by hand. + +## Estate transfers + +The `studio` participant reports editable project IDs and counts through the +existing estate screens. It moves property to the estate's character recipient; +WAITING/FROZEN property stays with the testator. Death, owner reference, exact +revision and current decision are checked before publication and source removal. +The source and recipient must finish pending studio saves before transfer starts. + +A planned destination is written and acknowledged before removing the source. +Repeated settlement reports the same cumulative moved IDs. Completed projects +are receipts and are not redirected automatically when staff later change a +recipient. A newer decision can replace an untouched plan. If any output was +published under an older decision, that partial transfer stays BLOCKED for staff +recovery. Affected libraries refuse conflicting edits while an intent is pending. +Transient disk failures return RETRY; conflicting contents return BLOCKED. + +Inspect an estate's files before staff retry or waive a blocked participant: + +```sh +python3 tools/studio-ownership.py \ + --studio /path/to/plugins/MusicalInstruments/studio --kind estates +``` + +Use the existing `/estate show`, `/estate retry` and `/estate waive` procedures +only after the reported cause is understood. Do not delete a journal to force +retry: it contains the original ownership and transfer receipts. + +## Offline rollback + +Coordinate with the server deployment owner and stop Paper cleanly before apply. +Archive the current studio directory first; this retains new projects and edits +that restoring an old snapshot would otherwise discard. Preview estates first, +then legacy migration, with the inspection commands above. + +The same tool applies checked restoration only when current source/destination +contents still match their planned hashes: + +```sh +python3 tools/studio-ownership.py \ + --studio /path/to/plugins/MusicalInstruments/studio --kind estates --apply +python3 tools/studio-ownership.py \ + --studio /path/to/plugins/MusicalInstruments/studio --kind migration --apply +``` + +It restores exact original source bytes, removes only the unchanged planned +copy, and archives fully restored journals under `ownership/rolled-back/`. +Changed or missing copies produce conflicts and remain preserved for staff. +Projects created after migration are listed and retained; an older jar cannot +read their character namespace, so staff must reconcile them separately. +Published editions and account quota logs are never rolled back by this tool. + +After reconciling conflicts, restore the matching old jar. Re-upgrading runs a +new reviewed plan for journals that were fully rolled back. Keep archived +current data and old receipts; never make both legacy and character namespaces +simultaneously authoritative. + +## Verification and limits + +Prepare both pinned dependency manifests using the +[shared plugin installer](../TLibs/README.md#builds-and-releases), then run +from the source checkout. The fixture manifest is +`.github/compatibility/tlibs-2.3.xml` and should use a separate +`.build/legacy-plugin-dependencies.json` lock file. + +```sh +tfmc-mvn -o -B clean verify +python3 -m unittest discover -s tools -p 'test_*.py' +``` + +Tests cover two characters on one account; noncurrent outgoing-session cleanup; +late save routing; publication quota isolation; exact published bytes; unknown +fields through migration, editing and restart; inherited overflow; repeated +migration/estate settlement; unavailable identity; and failures before/after +destination publication and source removal. Stale revisions hold partially +published files instead of completing an obsolete handover. + +Ownership transfers operate only on durable project files. Publishing or copying +a physical disc still crosses Paper inventory/world persistence boundaries; +these tests do not establish arbitrary-crash exactly-once item delivery. A source +archive and journal must be preserved for staff recovery when a disk failure +leaves an ambiguous publication. From 2c8c0fe38f5e1eba6fe6d296a5e66e49d19019b9 Mon Sep 17 00:00:00 2001 From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com> Date: Tue, 13 Oct 2026 23:01:23 +0000 Subject: [PATCH 2/5] docs: clarify studio replay and recovery guards --- .../MusicalInstruments/studio-ownership.md | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/projects/MusicalInstruments/studio-ownership.md b/projects/MusicalInstruments/studio-ownership.md index 2a4980e..f37ce4a 100644 --- a/projects/MusicalInstruments/studio-ownership.md +++ b/projects/MusicalInstruments/studio-ownership.md @@ -95,7 +95,14 @@ are receipts and are not redirected automatically when staff later change a recipient. A newer decision can replace an untouched plan. If any output was published under an older decision, that partial transfer stays BLOCKED for staff recovery. Affected libraries refuse conflicting edits while an intent is pending. -Transient disk failures return RETRY; conflicting contents return BLOCKED. +Every entry, including historical completed receipts, validates its captured +death, full source/recipient references and integral revision. A previously +transferred project that reappears at its source blocks every revision, including +WAITING/FROZEN, while preserving both files and receipts for staff review. +Before/after YAML may differ only in the project authority fields: titles, tracks, +authorship and unknown nullable metadata remain unchanged even when a modified +plan carries recomputed hashes. Transient disk failures return RETRY; conflicting +contents return BLOCKED. Inspect an estate's files before staff retry or waive a blocked participant: @@ -115,18 +122,24 @@ Archive the current studio directory first; this retains new projects and edits that restoring an old snapshot would otherwise discard. Preview estates first, then legacy migration, with the inspection commands above. -The same tool applies checked restoration only when current source/destination -contents still match their planned hashes: +The offline tool requires Python 3 and PyYAML (`python3-yaml` on Ubuntu). Its safe +loader rejects duplicate YAML fields, and duplicate XML keys are also rejected. +It preflights every entry in a journal before restoring any project, checking +full references, captured decisions, paths, hashes and the authority-only +before/after transformation. Current source/destination contents must still +match their planned hashes. Apply requires a new recovery directory: ```sh python3 tools/studio-ownership.py \ - --studio /path/to/plugins/MusicalInstruments/studio --kind estates --apply + --studio /path/to/plugins/MusicalInstruments/studio --kind estates --apply --backup /path/to/new-estate-recovery python3 tools/studio-ownership.py \ - --studio /path/to/plugins/MusicalInstruments/studio --kind migration --apply + --studio /path/to/plugins/MusicalInstruments/studio --kind migration --apply --backup /path/to/new-migration-recovery ``` -It restores exact original source bytes, removes only the unchanged planned -copy, and archives fully restored journals under `ownership/rolled-back/`. +It preserves the current journal and every current project copy in the new +recovery directory before the first restore or removal. It restores exact +original source bytes, removes only the unchanged planned copy, and archives +fully restored journals under `ownership/rolled-back/`. Changed or missing copies produce conflicts and remain preserved for staff. Projects created after migration are listed and retained; an older jar cannot read their character namespace, so staff must reconcile them separately. From eadc903f60547277fd8565a9e6310efa993a631b Mon Sep 17 00:00:00 2001 From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com> Date: Wed, 14 Oct 2026 00:54:20 +0000 Subject: [PATCH 3/5] docs: explain studio overflow and unassigned reports --- projects/MusicalInstruments/studio-ownership.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/projects/MusicalInstruments/studio-ownership.md b/projects/MusicalInstruments/studio-ownership.md index f37ce4a..31aa2c4 100644 --- a/projects/MusicalInstruments/studio-ownership.md +++ b/projects/MusicalInstruments/studio-ownership.md @@ -20,7 +20,9 @@ Publication timestamps in `studio/editions/.log` and the short publication cooldown remain account-wide. Switching characters or inheriting another author's project does not reset them. An inherited library may exceed 36 projects; every inherited project remains available, and creating another -project waits until the character has fewer than 36. +project waits until the character has fewer than 36. The library menu shows only +the first 36. Use `/music projects` to list all project IDs and +`/music open ` to open a project beyond those menu slots. ## Requirements and storage @@ -76,8 +78,9 @@ python3 tools/studio-ownership.py \ --studio /path/to/plugins/MusicalInstruments/studio --kind migration ``` -The JSON report lists held files, publication states, conflicts, character -projects created after the plan, and legacy paths with no completed mapping. +The JSON report lists held files, publication states, conflicts and character +projects created after the plan. `unassigned_legacy` lists legacy paths with no +migration journal yet; incomplete journals appear under `journals`. Do not edit a source archive to choose an heir; fix the reviewed mapping before its first plan, or recover a previously planned transfer by hand. From 4a82b71a01a849e5de6a9309f5c4e36b26b9a20b Mon Sep 17 00:00:00 2001 From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com> Date: Wed, 14 Oct 2026 02:55:32 +0000 Subject: [PATCH 4/5] docs(studio): clarify migration holds and chained rollback --- .../MusicalInstruments/studio-ownership.md | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/projects/MusicalInstruments/studio-ownership.md b/projects/MusicalInstruments/studio-ownership.md index 31aa2c4..26b5c18 100644 --- a/projects/MusicalInstruments/studio-ownership.md +++ b/projects/MusicalInstruments/studio-ownership.md @@ -7,7 +7,10 @@ project library, selected project, pending takes and editing authority. Switchin characters or leaving finishes the outgoing take using its captured character, stops private previews and closes studio menus. Delayed menu/dialog responses check the initiating session; a response from an earlier session cannot edit the -incoming character's project or consume its disc. +incoming character's project or consume its disc. A running take refuses another +recording, project changes, deletion or selection until `/music stop`; the +recording guard uses the player account while its saved project retains the +captured character authority. The original song author and author account remain on the project. Inheriting a project grants editing authority without changing that attribution. Published @@ -69,7 +72,14 @@ interrupted runs to resume the same planned recipient. Original account files remain retained and inert. A recipient with a selected project keeps that selection; migration supplies the legacy selection only when none is present. Unsupported, corrupt or identity-mismatched records are held. A conflicting -recipient project is not overwritten. +recipient project is not overwritten. Readable migration failures hold only +their planned character and do not stop later accounts from migrating; one +initial attempt logs all account failures, and the hourly task retries them. +Unreadable or malformed ownership journals still refuse all studio edits and +transfers; a missing captured authority envelope is never treated as unrelated. Estates wait for their account’s migration to complete. +An invalid legacy `selected` pointer alone does not hold project inheritance +when its original invalid pointer bytes remain recorded and unchanged; missing +or conflicting pointer evidence and every other held record still require review. Inspect journals and unassigned legacy paths from the source checkout: @@ -98,6 +108,11 @@ are receipts and are not redirected automatically when staff later change a recipient. A newer decision can replace an untouched plan. If any output was published under an older decision, that partial transfer stays BLOCKED for staff recovery. Affected libraries refuse conflicting edits while an intent is pending. +An unfinished operation reserves its project IDs against every other migration +or estate journal before a new transfer makes backups or publishes. Exact own +recovery and unrelated transfers remain possible. A recorded published target +that disappears stays BLOCKED for staff recovery; it is never recreated from +the retained source as though its earlier publication had no effect. Every entry, including historical completed receipts, validates its captured death, full source/recipient references and integral revision. A previously transferred project that reappears at its source blocks every revision, including @@ -144,6 +159,13 @@ recovery directory before the first restore or removal. It restores exact original source bytes, removes only the unchanged planned copy, and archives fully restored journals under `ownership/rolled-back/`. Changed or missing copies produce conflicts and remain preserved for staff. +Estate chains can require multiple checked passes: journals are tried by mtime, +and an older journal updated later may be visited before its newer transfer is +restored. After a pass makes progress, run preview again; apply newly restorable +journals with a fresh recovery directory. Continue only while preview identifies +new safe restorations. Reconcile remaining unchanged conflicts manually, and +finish every estate chain before applying legacy migration rollback. There is +no automatic ordering or repeated-apply loop. Projects created after migration are listed and retained; an older jar cannot read their character namespace, so staff must reconcile them separately. Published editions and account quota logs are never rolled back by this tool. @@ -170,7 +192,10 @@ Tests cover two characters on one account; noncurrent outgoing-session cleanup; late save routing; publication quota isolation; exact published bytes; unknown fields through migration, editing and restart; inherited overflow; repeated migration/estate settlement; unavailable identity; and failures before/after -destination publication and source removal. Stale revisions hold partially +destination publication and source removal; distinct account/character recording +guards; cross-death and migration reservations; same-stat reopened journals; +published-target disappearance; account migration isolation; exact retained +pointer evidence; and multipass estate-chain rollback. Stale revisions hold partially published files instead of completing an obsolete handover. Ownership transfers operate only on durable project files. Publishing or copying From a345d3b0208e4ae868f0bcfa59f40bfd633c0ccd Mon Sep 17 00:00:00 2001 From: Ryan Barlow <7389646+ryanbarlow97@users.noreply.github.com> Date: Wed, 14 Oct 2026 04:28:04 +0000 Subject: [PATCH 5/5] docs: clarify studio read and save boundaries --- projects/MusicalInstruments/studio-ownership.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/projects/MusicalInstruments/studio-ownership.md b/projects/MusicalInstruments/studio-ownership.md index 26b5c18..0720baf 100644 --- a/projects/MusicalInstruments/studio-ownership.md +++ b/projects/MusicalInstruments/studio-ownership.md @@ -81,6 +81,20 @@ An invalid legacy `selected` pointer alone does not hold project inheritance when its original invalid pointer bytes remain recorded and unchanged; missing or conflicting pointer evidence and every other held record still require review. +An already-open editor or library can keep reading its current character's +projects during an ownership hold. Opening a menu or dialog and admitting a +change still check the current ownership journals. Estate reconciliation checks +current journals before an operation first makes a backup, writes a plan or +publishes a transfer; its pass snapshot is used only to identify operations with +no effects. Edits and recordings already accepted for the captured character +finish saving before that character's studio can transfer, including during +session end and clean shutdown. + +Each change still reads and hashes every current journal. Before a future Main +rollout, measure the retained history: at most 1,000 journals and a warmed scan +around 10 ms are the deployment gate. A larger or slower history needs a separate +exact-byte design and review before Main activation. + Inspect journals and unassigned legacy paths from the source checkout: ```sh