Skip to content

Per-phrase tiers 1/4: persist per-tier phrase data through the sloppak save path #33

Closed

Description

@cchacs

Part of #3. First: the storage foundation the other sub-issues write into.

Today _save_sloppak repopulates each phrase's levels[] by repeating the editor's single flat note/chord/anchor list across every level, which makes the mastery slider a no-op for editor-saved packs (see the CHANGELOG entry quoted in #3).

Scope

  • Give the editor's in-memory model a place to hold distinct per-tier content for a phrase (a full chart down to simplified versions, mirroring RS2014 <level difficulty="N"> blocks).
  • Update _save_sloppak to persist the authored per-tier data instead of repeating the flat list across every levels[] entry; a phrase with no authored tiers keeps today's behavior.
  • Check the on-disk shape against the pack format (feedpak-spec, lib/song.py Phrase/PhraseLevel) before writing; any new manifest key must land in the spec first.
  • Round trip: load a sloppak with distinct levels, save without edits, and confirm the levels are preserved.

Acceptance criteria

  • A sloppak with distinct per-level data survives load and save unchanged.
  • A phrase without authored tiers still saves exactly as before.
  • Covered by backend tests.

Out of scope

Creating tiers (2/4 and 3/4) and the end-to-end slider check (4/4).

Activity

  1. pullfrog commented on Oct 2, 2026

    @pullfrog
  2. cchacs commented on Oct 2, 2026

    @cchacs
    CollaboratorAuthor

    @pullfrog please work on this issue. It is the first sub-issue of #3 and has no dependencies. Check the on-disk shape against feedpak-spec before writing any new field; any new manifest key must land in the spec first, so stop and say so if it would need one. Keep the PR scoped to this sub-issue only and do not start the dependent sub-issues. Open a PR against main; do not merge it.


    Generated by Claude Code

  3. pullfrog commented on Oct 2, 2026

    @pullfrog

    PR #47 is open against main (unmerged): #47 — storage foundation for #33, no tier-authoring UI.

    What changed. phrases[].levels[] is already the spec's per-phrase tier array, but the save path overwrote every level with one flat-chart slice, so a pack's distinct tiers were destroyed on any edit (and the highway's mastery filter then rendered stale per-level notes, making all edits vanish). Load now hands the editor phrases[].tiers[] in the editor's note/chord shape (_phrase_with_tiers), and save writes it back to the existing on-disk levels[] (_authored_phrase_levels): authored lower tiers persist verbatim, the top tier re-slices from the flat chart per core's top-level rule, and a phrase with no tiers keeps the old flat behavior. No new pack field. On the frontend, tier content rides the beat-primary tempo model (lift/reproject) and the save strip clears beat/beatEnd one level deeper, and replacing a chart drops tier content while keeping the difficulty ladder (undo stays exact).

    Review findings addressed. Fixed a typo I introduced in the hoisted wire converter (slide_unpick_to → slide_unpitch_to, which silently reset slu to -1 for flat and tier notes) and added a mechanical guard that every _NOTE_TECH_FIELDS name reaches the wire — verified it fails when the typo is reintroduced. Also: a tier member that can't convert is skipped instead of failing the whole save (that payload path is new and client-supplied), per-tier handshapes now get the arrangement's chord_id bound, unknown keys on an incoming level survive as on the flat path, a repeated difficulty keeps the first tier, and the two weak frontend tests were made non-tautological.

    Verification. pytest: 468 passed, 5 skipped (444 baseline + 24 new). npm test: 316 files, 0 failures. npm run lint: 0 errors, 3 pre-existing warnings.

    Not included (unchanged from the plan): tier-authoring UI, and per-tier MusicXML export — _build_arrangement_xml ignores phrases entirely, which is pre-existing and no route consumes it.

    Pullfrog  | View workflow run | via Pullfrog | Using big-pickle (free) | 𝕏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions