Files
bambuddy/backend
maziggy 90d66b7bba Keep both modes' slot assignments across an inventory mode switch (issue #2812)
Turning Spoolman mode on ran an unfiltered delete(SpoolAssignment) across every
    printer. Turning it straight back off cleared the other table instead, so the
    two directions were symmetric in code and one-way in effect, and the setting
    auto-saves on a 500 ms debounce with no save button and no confirmation.
    Opening the settings page to see what the option did was enough to destroy the
    configuration: the reporter's log shows four toggles in 85 seconds, which is
    someone looking and reverting, and the assignments never came back.

    The deletion was not careless. Checks that read both assignment tables would
    otherwise let a row in the mode you are not using answer for the mode you are,
    which is how #1473 was fixed, and emptying the inactive table made that
    impossible by construction. The cost was that the guarantee was bought with the
    user's data. That decision belongs to the readers -- the mode is a property of
    the install, not of the rows -- so spoolman_owns_assignments now answers it and
    nothing is deleted on a toggle. Each mode keeps its own assignments and
    switching is reversible. Existing installs need no migration: their inactive
    table is already empty, because it was being emptied.

    Six sites had to be told which mode they meant, and only two of them are the
    reads you would guess at, the missing-assignment notification and the queue cost
    estimate. The per-slot K-profile lookup consults the built-in table first and,
    on a hit with no matching profile, deliberately stops rather than falling
    through to Spoolman, so a leftover row would have shadowed the Spoolman binding
    for that slot -- the symptom #1556 reported from the other direction.
    configure_ams_slot *writes* a K-profile against whichever table answers first,
    so the same leftover would have filed a calibration against a spool the printer
    is not drawing on and never written the local one, leaving a calibration that
    appeared to succeed and then did not apply.

    The auto-unlink pass in on_ams_change is the one that would have made this
    change worthless. It drops any assignment whose tray no longer matches the
    fingerprint it recorded, and it ends in db.delete. Ungated, it would have
    removed the preserved rows one slot at a time as the AMS contents changed under
    the other mode -- the same loss, arriving slowly enough not to be connected to
    the toggle that caused it.

    The sixth is the built-in remaining-weight fallback inside the Spoolman AMS
    sync, and it is deliberately left inert rather than woken up. It could never
    fire while the table it reads was being emptied, it is keyed by slot rather than
    by spool, and create_spool writes remaining_weight unconditionally where the
    update path does not -- so preserving the rows would have seeded a stale figure
    into a brand new Spoolman spool the first time a tray reported an unusable
    remain%. The query stays, gated off, so the intent survives for whoever
    revisits the cross-mode fallback.

    Separately, a print that could not debit a spool said nothing about it, and that
    is what turned a mis-click into lost filament. The reporter's print was already
    running when they toggled. At completion it resolved its 3MF, read its
    per-filament grams, resolved its tray, and then skipped the debit because the
    assignment row no longer existed -- logged at INFO, invisible under the default
    log level, while the completion notification fired as usual. 65.49 g was never
    deducted and they only noticed because a spool's remaining weight looked wrong.
    _resolve_spool_id_for_tray has no tag or fingerprint fallback, so there was
    nothing else to catch it.

    The skip is now a warning naming the grams, and a completed print that failed to
    charge a tray it drew from raises the missing-spool-assignment notification. The
    print-start check cannot cover this and was right to stay quiet: the assignments
    existed when it ran. The two are different statements -- the first says the
    weight may not be tracked, the second says it was not -- so a print warned at
    start will notify twice, which is the right trade. Collected across the print
    rather than fired per slot, and given the caller's session, because this runs
    inside on_print_complete's transaction and opening a second one to read the
    printer's name would deadlock against it on SQLite. This is independent of the
    toggle and catches any other cause of an assignment disappearing mid-print.
2026-08-26 10:21:59 +02:00
..
2025-11-28 10:23:59 +01:00