mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-01 14:27:00 +00:00
v3.2: vendor-bundle re-mint under the product-triple rule
Every remaining legacy filament_id outside the BBL/QD_* islands re-derives from its product triple (filament_vendor / filament_type / family name), completing the content-addressed id model of filament_id_plan_v3.md: - Relocation pre-step: 14 fdm_filament_* template forks (Cubicon x3, Prusa x6, RH3D x6 minus the pc fork already re-homed) stop declaring ids; the 16 presets that rode Prusa's forks now declare the id they already resolved (verified zero effective-id drift over all 5892 instantiated presets). - --drop-redundant-ids: 6 Custom/MyToolChanger generics drop copied GF ids and ride their OFL families. - --remint over all 62 non-island vendors: 2647 declarations re-derived; identical products converge cross-bundle (showcase: PolyLite PLA is now OF5CgdDq in OrcaFilamentLibrary, Qidi, OrcaArena and Snapmaker). - Prusament @XL completions surfaced by the relocation: filament_vendor ["Prusa Polymers"] on the 8 @XL declarers; Prusament PA-CF typed PA11-CF (the product is Prusament PA11CF) and PC-CF typed PC-CF (-CF family typed as base polymer); each family converges on one id. - Succession: 331 ids retired with mode-rule successors, 355 never-shipped v1 mints forgotten with chain splicing, 151 GF-shaped ids released to the island space with hints, 27 curated BBL-generic -> OFL-generic hints (GFL99 -> OFDSrzZ8 class). New --retire "OLD=NEW" maintenance mode records lineage for OGFC99/OGFG99/OGFN99 (the shipped ids of OFL's Generic PC/PETG/PA in released versions, whose claims migrated in v3.1 while Cubicon's inert fork declarations kept them alive). - Retiring the P-hex system ids removes the last system ids from check_ams_filament_valid's destructive P-gate. - AMS-ambiguity fixes surfaced by convergence (validator -f): 4 presets riding another family's id through inherits now declare their true family id (Elegoo Generic ASA-CF/PETG-CF, Snapmaker PolyLite Dual PLA / PolyLite J1 PLA); 9 Cubicon @base presets and Dremel Generic PLA, which duplicate their per-printer variants on the same printers, split onto the salt-1 iteration of their triple (sanctioned by the mint-conformance check; --remint now leaves salt-conformant declarations alone). Gates: assign_filament_ids --check 0; orca_extra_profile_check 0; 116 python unit tests; config-equivalence over all 5892 instantiated presets (byte-identical configs except the 14 sanctioned Prusament vendor/type corrections; setting_id and compatible_printers unchanged; per-family convergence and ledger conservation verified); profile validator base/-f/-r(BBL)/-r(Qidi) all green; libslic3r_tests 48610 assertions; Moonraker OFL generic map check; custom-preset fixture archives v1.9.0-v2.4.1.
This commit is contained in:
@@ -60,6 +60,11 @@ Run from anywhere: python3 scripts/assign_filament_ids.py
|
||||
drops listed never-shipped ids from lineage instead)
|
||||
--add-hint "OLD=NEW"
|
||||
record a cross-island succession hint (standalone)
|
||||
--retire "OLD=NEW"
|
||||
retire a vanished non-island shipped id with an explicit
|
||||
successor (standalone; for lineage the claim vote can no
|
||||
longer see because the claims migrated while another
|
||||
declarer kept the id alive)
|
||||
--remint VENDOR re-derive VENDOR's declared ids from their triples and
|
||||
rewrite mismatches in place (repeatable)
|
||||
--drop-redundant-ids VENDOR
|
||||
@@ -1121,6 +1126,69 @@ def add_hints(pairs, profiles_dir=PROFILES_DIR, retired_path=RETIRED_PATH):
|
||||
return 0
|
||||
|
||||
|
||||
def retire_ids(pairs, profiles_dir=PROFILES_DIR, retired_path=RETIRED_PATH):
|
||||
"""--retire "OLD=NEW": retire a vanished non-island id with an explicit successor.
|
||||
|
||||
For shipped ids whose lineage the claim vote can no longer see: the id
|
||||
vanished from the tree in an earlier phase's shadow — another declarer kept
|
||||
it alive while its claims migrated — so no --update-snapshot run ever saw
|
||||
the id vanish while its claims could still vote. OLD must be absent from
|
||||
the tree (declared or effective) and outside every reserved id space
|
||||
(island ids forward via --add-hint; user-custom ids are never system ids),
|
||||
and must not already be retired or hinted. NEW must be a live tree id.
|
||||
Appends {"claims": [], "successor": NEW}. All pairs validate before
|
||||
anything is written. Returns 0 on success.
|
||||
"""
|
||||
_utf8_console()
|
||||
analysis = analyze_tree(profiles_dir)
|
||||
errors = 0
|
||||
for msg in analysis["read_errors"]:
|
||||
print_error(msg)
|
||||
errors += 1
|
||||
ledger = load_ledger(retired_path)
|
||||
live = set(analysis["ids"])
|
||||
declared_anywhere = set()
|
||||
for vids in analysis["declared_ids"].values():
|
||||
declared_anywhere |= set(vids)
|
||||
parsed = []
|
||||
for pair in pairs:
|
||||
if "=" not in pair:
|
||||
print_error(f'--retire expects "OLD=NEW", got "{pair}"')
|
||||
errors += 1
|
||||
continue
|
||||
old, new = pair.split("=", 1)
|
||||
is_reserved, _island = reserved_space_owner(old)
|
||||
if old in ledger["retired"]:
|
||||
print_error(f'--retire: "{old}" is already retired')
|
||||
errors += 1
|
||||
elif old in ledger["hints"]:
|
||||
print_error(f'--retire: "{old}" is a hint key; it already forwards')
|
||||
errors += 1
|
||||
elif is_reserved:
|
||||
print_error(
|
||||
f'--retire: "{old}" sits in a reserved id space; island ids '
|
||||
f"forward via --add-hint, user-custom ids are never retired")
|
||||
errors += 1
|
||||
elif old in live or old in declared_anywhere:
|
||||
print_error(
|
||||
f'--retire: "{old}" still lives in the tree; re-mint or delete '
|
||||
f"its declarers first (an id that vanishes normally gets its "
|
||||
f"successor voted by --update-snapshot)")
|
||||
errors += 1
|
||||
elif new not in live:
|
||||
print_error(f'--retire: successor "{new}" is not a live tree id')
|
||||
errors += 1
|
||||
else:
|
||||
parsed.append((old, new))
|
||||
if errors:
|
||||
return 1
|
||||
for old, new in parsed:
|
||||
ledger["retired"][old] = {"claims": [], "successor": new}
|
||||
print_info(f'retired: "{old}" -> "{new}"')
|
||||
write_ledger(retired_path, {"retired": ledger["retired"], "hints": ledger["hints"]})
|
||||
return 0
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Byte-preserving profile edits
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1336,6 +1404,9 @@ def remint_vendors(vendor_list, profiles_dir=PROFILES_DIR, retired_path=RETIRED_
|
||||
triple; rewrite mismatching declarations in place (byte-preserving). Never
|
||||
touches the snapshot — run --update-snapshot afterwards and review the diff.
|
||||
|
||||
A declaration already equal to ANY salt iteration of its own triple is
|
||||
mint-conformant (check 3) and left alone — deliberate salt splits (distinct
|
||||
presets of one product kept apart for per-printer AMS matching) survive.
|
||||
A candidate id is blocked when it is a retired key, a hints key, or occurs
|
||||
in the tree with any triple set other than exactly {T}. Equal-triple reuse
|
||||
is convergence: the same product must end up under the same id everywhere.
|
||||
@@ -1393,6 +1464,13 @@ def remint_vendors(vendor_list, profiles_dir=PROFILES_DIR, retired_path=RETIRED_
|
||||
if is_island_declaration(vendor, fid):
|
||||
continue
|
||||
scanned += 1
|
||||
# Any salt iteration of the declarer's own triple is already
|
||||
# mint-conformant (check 3) — leave it. This keeps deliberate salt
|
||||
# splits (two presets of one product that must stay distinct for
|
||||
# per-printer AMS matching, validator -f) stable across re-mints.
|
||||
if fid in {generate_filament_id(*rec["triple"], salt=s)
|
||||
for s in range(MAX_CHECK_SALT + 1)}:
|
||||
continue
|
||||
want = want_id(rec["triple"])
|
||||
if fid == want:
|
||||
continue
|
||||
@@ -1481,6 +1559,10 @@ def main(argv=None):
|
||||
help="record a cross-island succession hint (island-owned "
|
||||
"live id -> live id) in the shipped ledger; standalone, "
|
||||
"repeatable")
|
||||
parser.add_argument("--retire", metavar='"OLD=NEW"', action="append",
|
||||
help="retire a vanished non-island shipped id with an "
|
||||
"explicit successor (for lineage the claim vote can "
|
||||
"no longer see); standalone, repeatable")
|
||||
parser.add_argument("--remint", metavar="VENDOR", action="append",
|
||||
help="re-derive VENDOR's declared filament_ids from their "
|
||||
"triples and rewrite mismatches in place; repeatable")
|
||||
@@ -1507,10 +1589,16 @@ def main(argv=None):
|
||||
return 0
|
||||
|
||||
if args.add_hint:
|
||||
if args.update_snapshot or args.check or args.remint or args.drop_redundant_ids:
|
||||
if (args.update_snapshot or args.check or args.remint
|
||||
or args.drop_redundant_ids or args.retire):
|
||||
parser.error("--add-hint runs standalone")
|
||||
return add_hints(args.add_hint, args.profiles, RETIRED_PATH)
|
||||
|
||||
if args.retire:
|
||||
if args.update_snapshot or args.check or args.remint or args.drop_redundant_ids:
|
||||
parser.error("--retire runs standalone")
|
||||
return retire_ids(args.retire, args.profiles, RETIRED_PATH)
|
||||
|
||||
if args.remint:
|
||||
if args.update_snapshot or args.check or args.drop_redundant_ids:
|
||||
parser.error("--remint cannot be combined with other modes")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -153,6 +153,12 @@ class SyntheticTree:
|
||||
rc = afi.add_hints(pairs, self.profiles, self.retired)
|
||||
return rc, buf.getvalue()
|
||||
|
||||
def retire(self, pairs):
|
||||
buf = io.StringIO()
|
||||
with contextlib.redirect_stdout(buf):
|
||||
rc = afi.retire_ids(pairs, self.profiles, self.retired)
|
||||
return rc, buf.getvalue()
|
||||
|
||||
|
||||
def make_clean_tree():
|
||||
"""Baseline tree: OFL base+generic, a vendor family, a clean tuned generic."""
|
||||
@@ -1049,6 +1055,65 @@ class TestAddHint(SyntheticTreeCase):
|
||||
self.assertIn('expects "OLD=NEW"', out)
|
||||
|
||||
|
||||
class TestRetire(SyntheticTreeCase):
|
||||
def test_retires_vanished_id_with_successor(self):
|
||||
rc, out = self.t.retire(["OFOLD001=AX01"])
|
||||
self.assertEqual(rc, 0, out)
|
||||
self.assertEqual(load_json_file(self.t.retired)["retired"]["OFOLD001"],
|
||||
{"claims": [], "successor": "AX01"})
|
||||
errors, out = self.t.check()
|
||||
self.assertEqual(errors, 0, out)
|
||||
|
||||
def test_rejects_live_old(self):
|
||||
rc, out = self.t.retire(["AX01=OGFL99"])
|
||||
self.assertEqual(rc, 1)
|
||||
self.assertIn("still lives in the tree", out)
|
||||
self.assertEqual(afi.load_ledger(self.t.retired)["retired"], {})
|
||||
|
||||
def test_rejects_declaration_only_old(self):
|
||||
# The inert-fork case: an id declared by a claim-less root still lives
|
||||
# in the tree; the declaration must be deleted before --retire.
|
||||
self.t.write_preset("VendorA", preset("ZOLD @base", filament_id="ZDECL01",
|
||||
instantiation=False,
|
||||
filament_vendor="ZV",
|
||||
filament_type="PLA"))
|
||||
rc, out = self.t.retire(["ZDECL01=AX01"])
|
||||
self.assertEqual(rc, 1)
|
||||
self.assertIn("still lives in the tree", out)
|
||||
|
||||
def test_rejects_island_space_old(self):
|
||||
rc, out = self.t.retire(["GFXX99=AX01"])
|
||||
self.assertEqual(rc, 1)
|
||||
self.assertIn("reserved id space", out)
|
||||
|
||||
def test_rejects_user_space_old(self):
|
||||
rc, out = self.t.retire(["P1234ABC=AX01"])
|
||||
self.assertEqual(rc, 1)
|
||||
self.assertIn("reserved id space", out)
|
||||
|
||||
def test_rejects_already_retired_old(self):
|
||||
self.t.write_ledger(retired={"ROLD": {"claims": [], "successor": None}})
|
||||
rc, out = self.t.retire(["ROLD=AX01"])
|
||||
self.assertEqual(rc, 1)
|
||||
self.assertIn("already retired", out)
|
||||
|
||||
def test_rejects_hinted_old(self):
|
||||
self.t.write_ledger(hints={"HOLD": "AX01"})
|
||||
rc, out = self.t.retire(["HOLD=AX01"])
|
||||
self.assertEqual(rc, 1)
|
||||
self.assertIn("hint key", out)
|
||||
|
||||
def test_rejects_dead_successor(self):
|
||||
rc, out = self.t.retire(["OFOLD001=OFnope99"])
|
||||
self.assertEqual(rc, 1)
|
||||
self.assertIn("not a live tree id", out)
|
||||
|
||||
def test_rejects_malformed_pair(self):
|
||||
rc, out = self.t.retire(["OFOLD001"])
|
||||
self.assertEqual(rc, 1)
|
||||
self.assertIn('expects "OLD=NEW"', out)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# default run: mint + insert
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -1261,6 +1326,21 @@ class TestRemint(SyntheticTreeCase):
|
||||
changed, errors, _out = self.t.remint(["VendorA"])
|
||||
self.assertEqual((changed, errors), (0, 0))
|
||||
|
||||
def test_salted_declaration_survives_remint(self):
|
||||
# A deliberate salt split (a second preset of one product kept on its
|
||||
# own id for per-printer AMS disambiguation) is mint-conformant and
|
||||
# must not be converged back onto salt 0.
|
||||
salt1 = afi.generate_filament_id(*self.TRIPLE, salt=1)
|
||||
self.t.write_preset("VendorA", preset("APLA @legacy",
|
||||
filament_id=salt1,
|
||||
inherits="APLA @base",
|
||||
compatible_printers=["P2 0.4 nozzle"]))
|
||||
changed, errors, out = self.t.remint(["VendorA"])
|
||||
self.assertEqual(errors, 0, out)
|
||||
self.assertEqual(changed, 1) # only the non-conformant AX01 root
|
||||
legacy = load_json_file(self.t.preset_path("VendorA", "APLA @legacy"))
|
||||
self.assertEqual(legacy["filament_id"], salt1)
|
||||
|
||||
def test_same_triple_converges_within_run(self):
|
||||
self.t.add_vendor("VendorB", [
|
||||
preset("APLA @Bbase", filament_id="BX01", instantiation=False,
|
||||
|
||||
Reference in New Issue
Block a user