Compare commits

..

27 Commits

Author SHA1 Message Date
SoftFever
f23e5875d5 filament_id v4.2b: re-mint the Qidi QD_* island (plan v4 SS4)
All 264 QD_* declarations re-derive from their family triples via
--remint Qidi. Only 8 family ids are new: most QIDI-brand products already
carried v3.2-minted ids on older printer series, so the QD-series
declarations converge onto them (QD_0..4_1_1 -> OFKIQO2W "QIDI PLA
Rapido"), and the six Generic families converge onto the shared generic
ids (Generic PLA -> OFDSrzZ8 etc.) by triple math -- no inherits or
compatible_printers changes anywhere.

--update-snapshot retires all 204 QD_* ids with mode-rule successors.
Audit: 204/204 non-null successors, every chain ends at a live id; the
four ids hardcoded in QidiPrinterAgent's non-numeric-series fallback
resolve to the OFL generics (QD_1_0_1 -> OFDSrzZ8, ...). The v4.1 agent
hook translates device-composed QD_* ids through these entries at
runtime, so box behavior is preserved on updated clients.

Known accepted residue (plan v4 SS4): three case/spelling family splits
keep distinct ids for one product line each -- QIDI PC-ABS-FR (series
1-2) vs QIDI PC/ABS-FR (series 3-4) vs Qidi PC-ABS-FR (X-Plus 4), and
QIDI TPU 95A-HF vs Qidi TPU 95A-HF. Renames are ruled out; the ledger's
per-series successors keep each device slot resolving to the right
presets, and a future upstream name unification self-heals through
content addressing.

The code<->ledger lockstep test is now active (un-skipped).

Gates: --check 0; orca_extra_profile_check 0/0; 117 script tests OK
(0 skipped); validator base, -f tree-wide, -v Qidi all exit 0;
libslic3r_tests green; Qidi profile diff is filament_id-value-only.
Qidi.json version bumped.
2026-08-21 18:36:28 +08:00
SoftFever
8d83414d09 filament_id v4.2a: dissolve the QD_* island in the tooling (plan v4 SS3)
- is_island_declaration: only BBL remains an island; Qidi QD_* declarations
  now enter the triple bookkeeping (checks 3 and 8, --remint's domain).
- reserved_space_owner: QD_* stays reserved but ownerless -- no vendor may
  declare it, --update-snapshot refuses new QD_* ids outright, and vanished
  QD_* ids take the retired-with-successor path instead of the island
  release-with-hint path. New reserved_space_desc() names the space in
  check 6 / sanction-gate messages.
- Check 1 drops the vendor-Qidi QD_* format exemption; --add-hint now
  refuses QD_* keys (island space is GF* only); docstrings updated.
- Snapshot regenerated: the 204 QD_* declarer triples are now recorded
  (declared triples 1113 -> 1317); ids/claims unchanged.
- Tests updated for the flip; new (skipped until v4.2b) lockstep test
  asserting the QidiPrinterAgent fallback QD_* literals stay retired
  ledger keys chaining to live ids.

Tree state is untouched: presets still declare their QD_* ids, all
grandfathered by the snapshot. --check exit 0; 117 script tests OK.
2026-08-21 18:34:20 +08:00
SoftFever
1c405f8a2c filament_id v4.1: resolve retired QD_* ids in the Qidi box miss path
QidiPrinterAgent composes QD_<series>_<vendor>_<typeidx> setting ids from
device enums and previously degraded any slot whose id matched no visible
preset to generic-by-type. Plan v4.2 retires every QD_* preset id to the
succession ledger, so insert the ledger walk between the direct match and
the generic fallback: a composed QD_* id now forwards to its family's
minted OF* successor exactly like every other retired id.

Behavior-neutral until the ids are actually retired (the ledger holds no
QD_* keys yet, and live QD_* presets still match directly).

Also documents the code<->ledger lockstep on the hardcoded non-numeric-
series fallback table (QD_1_0_1/_11/_41/_50) and adds a C++ test pinning
that the succession walk is key-format agnostic.

Gates: libslic3r_tests [filament_id] 14 assertions green; libslic3r_gui
compiles.
2026-08-21 18:34:20 +08:00
SoftFever
58d1a3b093 filament_id v4.0: re-converge post-merge drift (Qidi/Snapmaker/re3D)
The 2026-08 main merge brought upstream vendor updates in pre-v3 style;
assign_filament_ids.py --check reported 243 errors. Re-converged per plan
v4 SS1(A)/SS4:

- Qidi X5: 12 brand presets had copied BBL catalog ids (GFB99/GFG99/GFL99);
  re-minted to their product triples (PolyLite PLA converges onto OF5CgdDq).
- Snapmaker U1: fixed empty/lowercase/wrong filament_vendor on 5 bases
  (triple inputs, W3-style); re-minted 24 declarations, resurrecting no
  retired ids (11813638720/141703112701/1417031127011/OGFL99 all re-minted
  away; base2 duplicates converge onto their families' v3.2 ids).
- re3D: rPETG had copied GFG01 (BBL space); re-minted to OFmCOW2Y. The
  GreenGate3D bundle was removed upstream with its product relocated here;
  retired OFSTCno1 -> OFmCOW2Y so shipped references keep resolving.
- validator -f: resolved 34 AMS-match ambiguities using the v3 mechanisms:
  salt-1 ids on the 5 re3D root presets and Snapmaker PLA-CF @U1 0.4
  (duplicate presets of one product; deletion forbidden), own minted ids
  for the 4 cross-family riders (Polymaker General/Silk/Tough PLA Family,
  Snapmaker PLA Full Spectrum).
- Snapshot re-sanctioned (+92 ids incl. 44 new QD_4_* island ids and BBL
  addnorth GF_AN*); ledger +1 entry (OFSTCno1).

Gates: --check 0; orca_extra_profile_check 0/0; 116 script tests OK;
validator base + -f exit 0. Vendor versions bumped (Qidi, Snapmaker, re3D).
2026-08-21 18:13:18 +08:00
SoftFever
71563cc6c6 Add filament_id plan v4: dissolve the Qidi QD_* island 2026-08-21 18:13:18 +08:00
SoftFever
f3fa3a34bd Merge branch 'main' into feature/filament_id 2026-08-21 17:30:12 +08:00
SoftFever
1a62a25d36 Merge branch 'main' into feature/filament_id
Brings in 560 commits (merge base 2026-07-04 .. origin/main af9fd10d7a).
19 conflicts resolved; the other 138 touched files auto-merged.

Conflict resolutions
- Snapmaker/Polymaker (7): main normalised JSON key order in #15039 while this
  branch inserted filament_id/filament_vendor. Took main's key order and kept
  this branch's values, inserting a single filament_id key rather than letting
  git's line merge leave duplicate "from"/"instantiation" keys.
- re3D rPETG @0.8/@1.75 nozzle (2): main renamed these from "re3D Greengate
  rPETG @*" and re-vendored GreenGate3D -> re3D (#15169). Git's rename-aware
  merge produced a file with two filament_vendor keys; took main's version
  wholesale instead. The id is reconciled by the tooling, not by hand.
- re3D rPP (1): kept main's inherits change (fdm_filament_pet -> fdm_filament_pp,
  local filament_type override dropped) and its widened compatible_printers.
  The flattened type is still PP, so the product triple and id OFfHGM1D are
  unchanged.
- re3D Greengate rPETG.json, Afinia {ABS+,ABS,PLA,TPU,Value ABS,Value PLA}.json
  (7 modify/delete): accepted main's deletions. The Afinia ids are not orphaned
  - the surviving @HS presets resolve the same triple and already carry the
  same ids, so nothing is retired.
- .github/workflows/check_profiles.yml: kept both main's new "validate slice"
  step and this branch's tree-wide filament-subtype check.
- tests/libslic3r/CMakeLists.txt: kept both test_filament_id_succession.cpp and
  main's test_preset_diff.cpp.

Verified
- No conflict markers; all 12795 profile JSONs parse with no duplicate keys.
- All branch artifacts (tooling, snapshot, ledger, doc, tests) intact and
  unmodified by the merge.
- The succession-ledger C++ call sites survived main's refactors; audited
  Preset.{cpp,hpp}, PresetBundle.cpp, DeviceManager.cpp, PresetComboBoxes.cpp,
  CaliHistoryDialog.cpp, MoonrakerPrinterAgent.cpp against base/ours/theirs.
- scripts/tests: 115/116 pass.

Known follow-up: assign_filament_ids.py --check reports 243 errors, all from
filament data main added in the last month (BBL/addnorth, Qidi X5/Plus 5,
Snapmaker U1, re3D). The single failing unit test is the live-tree conformance
test asserting that count is zero. Reconciliation lands separately.
2026-08-08 02:05:37 +08:00
SoftFever
f7c1b290fd 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.
2026-07-04 21:09:29 +08:00
SoftFever
e3ac835cc3 v3.2 data fixes: per-vendor (filament_vendor, filament_type) reconciliation
Reconcile every same-family triple divergence ahead of the v3.2 re-mint,
per the pinned decision rule (spool brand; "Generic"-named -> Generic;
brand-named -> that brand in every bundle; first-party lines -> printer
brand; web evidence beats names). All 32 grandfathered triple_exceptions
resolved plus swept same-class finds; every bundle re-verified split-free
by loader-faithful re-resolution and adversarially reviewed.

- Flashforge: 10 exceptions + swept Flashforge PLA; FusRock S-Multi/S-PAHT
  illegal types decided with product evidence (PA / PAHT); FusRock Generic
  roots -> Generic; 57 explicit Generic pins keep 14 uninvolved 0.25-nozzle
  families triple-identical.
- Prusa: Prusament vendor -> "Prusa Polymers"; Generic ASA -> Generic;
  Generic TPU/TPU HF type FLEX -> TPU; fdm_filament_pc re-home (sanctioned):
  Prusa Generic PC family = OF97ru74, Prusament PC Blend = OFvDbkFq on the
  @XL pair; transitional ids deleted from OFL and Prusa fdm_filament_pc.
- Anycubic: 8 first-party families -> Anycubic on fdm_filament_common
  leakers; Generic PETG outlier -> Generic.
- Creality: 5 Ender-5Max/K2 generics + swept Creality Generic PA -> Generic.
- Snapmaker: PolyLite PLA -> Polymaker; PLA Lite child aligned to root per
  Snapmaker/Orca_Presets evidence.
- Sovol/re3D: Polymaker PETG / SUNLU PETG -> brand; Greengate rPETG ->
  GreenGate3D per manufacturer evidence.

Snapshot/ledger intentionally untouched here: the central mechanical
commit regenerates them; --check is expected red between the two.
2026-07-04 20:20:03 +08:00
SoftFever
23386f6129 Merge branch 'main' into feature/filament_id 2026-07-04 19:41:38 +08:00
SoftFever
3ccc58a9fc fix 2026-07-04 19:21:19 +08:00
SoftFever
acd5dd0794 v3.1: OrcaFilamentLibrary re-mint under the product-triple rule
Phase v3.1 of filament_id_plan_v3.md: every OFL-declared filament_id
re-derives from its product triple; the succession ledger absorbs the old
ids. Config-equivalence verified: flattened effective configs differ ONLY
in filament_id (284 consistent old->new changes tree-wide, including every
vendor preset that rides an OFL family), zero config/compatible_printers/
setting_id drift.

Structural pre-step (id-value-neutral, verified): OFL generic family ids
move from the shared fdm_filament_* template bases onto the product-named
"Generic X @System" presets, so the v3 triple's family component is the
product name, not an internal file name (12 relocations; 2 dead duplicate
declarations on fdm_filament_pa/pet pruned; every consumer chain itemized
first - the +12 instantiated_with_id entries are this deliberate pattern,
matching the 11 generics that already declared on @System).
fdm_filament_pc keeps its declaration this phase: 7 Prusa presets (Prusa
Generic PC, Prusament PC Blend) inherit it directly and re-home in the
v3.2 Prusa worksheet; its transitional id is documented in the plan.

Re-mint: 301 declarations re-derived (e.g. Generic PLA OGFL99 ->
OFDSrzZ8 = mint("filament_product/Generic/PLA/Generic PLA")). Snapshot:
1477 ids (+300/-292). Ledger:
- 230 shipped ids retired with mode-rule successors (OGF* library ids,
  OFLSBS99, DREMC/FILAR/AliZ/eSUN/... legacy strings). DREMC010 had been
  shared by a PPA-CF and a TPU family - a data bug this split resolves;
  its successor follows its only shipped claim (DREMC PPA-CF).
- 25 GF-shaped ids OFL had copied from the Bambu catalog (GFOT00x
  Overture, GFSEP0xx) are RELEASED to the BBL island space with hints at
  the re-minted families - never retired, so a future legitimate BBL
  catalog addition is never blocked; plus an explicit GFOT001 ->
  OFxA1p01 hint (Overture PLA Pro, still live in BBL).
- 37 never-shipped v1 branch mints dropped from lineage
  (--forget-never-shipped; they exist in no release, no forwarding
  needed).
- OGFC99/OGFG99/OGFL96/OGFN99/OGFSNL08 stay live (still declared by
  vendor bundles); they retire in v3.2 when those declarers re-mint.

Gates: 106 unit tests OK; --check exit 0; orca_extra_profile_check exit 0;
validator -l 2 / -f tree-wide / -r BBL+Qidi exit 0; Moonraker OFL map
check resolves all 29 aliases against the re-minted presets; flatten
equivalence pre/post as above; snapshot regen idempotent.
2026-07-04 16:41:41 +08:00
SoftFever
2c0867619c v3.0: content-addressed mint tooling, succession runtime, W1 hardening
Implements phase v3.0 of filament_id_plan_v3.md - tooling and client
prerequisites. No filament_id values change in this commit.

Tooling (scripts/assign_filament_ids.py; 106 unit tests):
- The mint key becomes "filament_product/<filament_vendor>/<filament_type>/
  <family_name>", resolved loader-faithfully from the declarer's flattened
  config - bundle-independent and content-addressed; identity fixes re-id
  by design and are made safe by the succession ledger. --mint now takes
  the triple.
- The snapshot gains "triples" and "triple_exceptions" sections, folded
  into the equality gate: any vendor/type/name change surfaces as a
  reviewable snapshot diff. Check 3 rewritten to triple-mint conformance
  with (id, triple) grandfathering; check 5 generalized from OFL generics
  to every OFL-riding vendor preset; new check 8 (triple integrity) and
  check 9 (succession integrity).
- The retired ledger moves to resources/profiles/retired_filament_ids.json
  so it ships with the app; schema {claims, successor} plus cross-island
  "hints". The 14 v1 entries are migrated with mode-rule successors.
  Vanished ids in a foreign island's space (GF*/QD_*) are RELEASED with a
  hint instead of retired: the island catalog owns them and may
  legitimately (re)ship them, which check 4 must never block.
- New maintenance modes: --remint VENDOR, --drop-redundant-ids VENDOR,
  --add-hint "OLD=NEW", and --update-snapshot --forget-never-shipped FILE
  (never-shipped ids drop from lineage; chains splice through them).

Runtime (C++):
- Succession helpers in libslic3r/Preset (pure chain-follow + lazy ledger
  load from resources), consulted only on resolution miss in
  get_filament_by_filament_id, both AMS sync predicates,
  add_ams_filaments, setting_id_to_type, and the calibration-history
  lookup; behavior is byte-identical while the ledger has no matching
  entry. Catch2 coverage in tests/libslic3r.
- W1 hardening: check_ams_filament_valid no longer remote-wipes trays or
  rewrites temps for P-shaped ids that a system preset carries (ten such
  system ids ship today); the size==8 && [0]=='P' assert is relaxed; the
  unguarded filament_list find deref in the temp-equation check returns
  non-destructively on a miss.
- MoonrakerPrinterAgent: the 23 hardcoded OFL generic ids are replaced by
  a runtime lookup of "Generic <family> @System" (id-equivalent on
  today's shipped profiles, follows future re-mints automatically);
  scripts/test_moonraker_lane_data.py derives its expectations from the
  shipped profiles and gains --check-ofl-map.

Profile data (W3 - type is now a key component, so type bugs are fixed
before any re-mint):
- 17 same-name-different-type groups corrected across 50 files (OFL
  Generic PETG-CF/PE-CF/PP-CF; Flashforge ASA Basic/ASA-CF/ABS-CF/HIPS/
  PAHT-CF/PLA Silk; FusRock PAHT; Creality Generic PA6-CF; InfiMech PETG;
  Anycubic TPU 95A / TPU for ACE; Prusa Generic PA-CF/PLA-CF) - every
  value validated against MaterialType::all(); 3 Snapmaker U1 roots gain
  their missing filament_vendor. Flattened-config equivalence vs the
  previous commit: exactly the 54 intended diffs, zero BBL.
- doc/developer-reference/filament_id.md rewritten for the v3 rule.

Ambiguous type divergences (Prusa Generic TPU/TPU HF FLEX-vs-TPU, FusRock
S-Multi/S-PAHT) and all same-type vendor-tag divergences are deferred to
the v3.2 vendor worksheets, catalogued with analysis.
2026-07-04 16:28:56 +08:00
SoftFever
67406b24ba plan v3: OFL as the catalog, content-addressed product ids
Revises the v1 mint rule after the maintainer catch that its key scoped
families to the profile bundle (printer brand), fragmenting one commercial
product into N ids (PolyLite PLA: five bundles, all filament_vendor
"Polymaker", up to five ids).

Architecture: BBL and QD_* stay frozen islands; OrcaFilamentLibrary becomes
the single declaration point for every other material family; vendor
bundles carry only same-alias specializations (no filament_id key) that
shadow the OFL preset per printer and resolve its id through the loader
walk. New mint key: uuid5 over
"filament_product/<filament_vendor>/<filament_type>/<family_name>" from the
root's flattened config — bundle-independent (hoisting families into OFL is
id-stable), and the type component keeps the four known same-name-
different-type groups apart until their data bugs are fixed.

Content-addressing replaces "ids immutable once shipped": identity edits
re-id the family, made safe by turning retired_filament_ids.json into a
shipped succession ledger (old id -> successor, chains allowed,
cross-island hints permitted) consulted on resolution miss in the AMS sync
and lookup paths. The MoonrakerPrinterAgent hardcoded generic-id map is
replaced by a runtime preset lookup, removing the code/profile lockstep.

Migration: v3.0 tooling + W1 client hardening + W3 type fixes; v3.1 OFL
re-mint with succession entries; v3.2 vendor bundles (391 unshipped OF ids
re-derive without retirement; generic tunings re-point to OFL; ~800 shipped
legacy ids re-mint with succession, incl. the 57 multi-vendor GF residue
and the 10 P-hex system ids, which also exits them from the destructive
check_ams_filament_valid P-gate); v3.3 optional id-stable consolidation
into OFL. Gate battery unchanged plus succession-resolution tests.

filament_id_plan_v2.md gets a status note: its P+md5 verdict stands; its
work items are absorbed or superseded by v3.
2026-07-04 14:54:48 +08:00
SoftFever
a395701f29 plan: validate the P+md5 (get_filament_id) mint proposal; record verdict as plan v2
Examined minting system filament_ids with CreatePresetsDialog.cpp:487's
user-custom allocator (adopt-by-base-name, else "P"+md5(name)[0:7]) for
Bambu AMS-sync compatibility. Verdict: keep the OF* mint; capture the
proposal's value through bounded work items instead of a re-mint.

- The device path never validates id shape: tray_info_idx is an opaque
  string end-to-end (DeviceManager.cpp:1642; DevFilaSystem.cpp:512-514)
  and firmware persists arbitrary bytes (bambulab/BambuStudio#5436).
  Resolution is by value against the GF catalog plus the account's cloud
  custom cache; unknown ids show "?" regardless of shape, and system
  presets can never enter that cache (Preset.cpp:2071 gates upload on
  is_user()).
- The only id-shape dispatch in the tree is destructive for P-shaped ids:
  check_ams_filament_valid (DeviceManager.cpp:5335/5352/5395/5410) remotely
  clears an AMS tray (:5345) or rewrites its temps when a P-shaped tray id
  drops out of the user-root preset list — a state name-keyed minting
  creates by construction. GF* and OF* ids are structurally immune.
- The real value is captured without re-minting 391 unshipped ids across
  1327 files: the adopt step == curated GF adoption via the snapshot ledger
  (W4); the hash belongs to user presets (W5, cf. PR #13315); client
  hardening (W1) that the ten already-shipped P-hex system ids (Cubicon x8,
  Ginger, Artillery) need today anyway.

Method: 6 parallel evidence agents (generator semantics, AMS/device path,
exhaustive shape-dispatch sweep, upstream + online sources, tree-wide
dry-run over 5892 presets / 1148 base names, branch change inventory) into
a 3-judge panel (keep-OF* won 23/40 aggregate); the upstream generator was
verified byte-identical to BambuStudio master, and every load-bearing
file:line and number in the document was re-verified against the tree
before commit.
2026-07-03 10:12:21 +08:00
SoftFever
ac12dff1af CI: run the filament subtype check tree-wide
With every vendor's filament_id collisions fixed (356 collision groups /
1256 printer-level ambiguity errors across 30 vendors, plus the 16
library-internal ids and the 10 live library-cross groups), the
duplicate-filament-subtype validation no longer needs the BBL-only scope:
drop -v BBL from the -f step so any new ambiguity in any vendor fails CI.

Local verification of the full workflow against this branch:
- extra JSON check exit 0; validator -l 2 exit 0 (66 vendors);
  -f tree-wide exit 0 (with the library-aware extended validator, which is
  strictly stricter than the released binary CI downloads); -r exit 0 for
  BBL and Qidi.
- custom-preset fixture archives v1.9.0..v2.4.1 overlaid per the workflow:
  six pass outright; v2.3.1/v2.3.2/v2.4.0/v2.4.1 fail locally only on a
  pre-existing Windows-only validator limitation (a user preset with a
  non-ASCII filename reads as empty; reproduced byte-identically on the
  pre-migration base commit, and absent on CI's Linux runners where these
  archives validate green).
2026-07-03 01:10:54 +08:00
SoftFever
33923464ae Cubicon: keep @base presets selectable; mint the variant tier instead
The custom-preset fixture validation (check_profiles.yml step 5, run locally
against the v2.3.2/v2.4.0/v2.4.1 archives) caught a real regression in the
previous Cubicon fix: those archives contain user presets whose inherits
names the @base presets directly ("can not find parent Cubicon ABS @base").
A system preset with instantiation:false is not added to the preset
collection, so flipping the nine @bases was equivalent to deleting their
names - exactly the user-preset drop hazard the migration rules forbid.

Repair: restore all nine @bases byte-identically to their pre-flip state
(instantiated, setting_id, original P510cf* ids; Cubicon PC @base keeps its
minted OFnLnZQo from the copy-paste fix). The AMS ambiguity is instead
resolved Dremel-style: each family's three passthrough variants share one
fresh variant-tier mint as own keys, so on every printer the base and the
variant carry different ids. Zero preset-set change, zero config change,
user presets and fixtures resolve exactly as before the migration.

Verified: fixture archives v2.3.2/v2.4.0/v2.4.1 no longer report missing
parents; config-equivalence holds; orca_extra_profile_check.py exit 0;
assign_filament_ids.py --check exit 0; validator -l 2 exit 0; tree-wide
extended -f still exit 0.
2026-07-03 00:51:29 +08:00
SoftFever
55a3114ec9 Long-tail vendors: family filament_ids (17 vendors, 41 collision groups)
Phase 2 completion - Volumic, Ratrig, Chuanying, Afinia, Eryone, FLSun,
Tiertime, Blocks, CONSTRUCT3D, Co Print, CoLiDo, DeltaMaker, Ginger
Additive, OrcaArena, Peopoly, Wanhao France, iQ. All are P1 copy-paste or
small structural fixes; owners keep their ids by Bambu-catalog/OFL/historic-
introducer precedence, impostor families get fresh deterministic mints,
zero effective-config change:

- Volumic 10 mints (ABS/ASA/PP, PETG/PCTG/ESD, PLA/UNIVERSAL, PA/PPS lines);
  Ratrig 10 (BigNozzle/PunkFil lines off the Generic line's ids); Chuanying
  8 (plus 3 redundant own-id lines removed so parents' minted ids flow to
  the 0.25-nozzle variants); FLSun 4 (S1/T1 High Speed + Silk); Peopoly 4
  (Lancer lines off Generic PLA's GFL99); Afinia 3 (Value PLA/ABS+/Value ABS
  inside the frozen GFx##_## scheme); Co Print 3 (ABS/PETG/TPU off GFL99).
- Tiertime: Generic SBS had copy-pasted Generic PLA's ids on both printer
  lines - one family mint on both variants collapses the split. Eryone: PP
  copy-pasted PETG-CF's EFL43 (same introducing commit) - PP minted.
  Blocks: ASA-CF copy-pasted PLA-CF's BSFI010 - ASA-CF minted. CONSTRUCT3D:
  High Flow PETG minted off GFG99. CoLiDo: both claimants of the invented
  GFA99 re-minted. DeltaMaker: Brand PLA minted off GFL99. OrcaArena:
  Generic PLA Silk minted; the Bambu-clone Arena PLA Silk keeps GFA05
  (catalog precedence). iQ: Grauts HPP4GF25 minted off Fiberthree's IQM1.
- Ginger Additive (P5): the shared fdm_filament_common template carried
  P510eff9; moved onto the two pellet families (Generic PETG keeps it as
  first claimant, Generic PLA minted), template key removed after verifying
  every inheritor still resolves an id.
- Wanhao France (P4): YUMI PLA Bowden stops over-claiming the six
  direct-drive printers covered by YUMI PLA Direct Drive (trim only, no
  mints).

Verified: config-equivalence gate zero unexpected diffs; extra check exit 0;
--check exit 0; validator -l 2 exit 0; and with all 30 vendor migrations
now applied, the extended validator's -f check is clean TREE-WIDE
(0 ambiguity errors, down from 1256 baseline / 16448 with the library-aware
extension); -r exit 0 for BBL and Qidi.
2026-07-03 00:16:01 +08:00
SoftFever
ee125975cf Snapmaker, FlyingBear, Sovol: family filament_ids
Phase 2 long-tail migrations (25 collision groups), zero effective-config
change:

Snapmaker (11 + the tree's one live library-cross group): three Benchy demo
presets coexisting via compatible_prints gating (invisible to the -f check)
get their own mints; the TPE/TPU-High-Flow lines riding shared bases get
family mints across the plain/U1/Dual/J1 variants; 'Snapmaker PLA Matte @U1'
minted; 'Snapmaker PET @Dual' unified onto its own family root id;
'Snapmaker PA-CF @U1' had its compatible_printers triple-duplicated
(self-collision) - deduplicated. The PolyTerra J1/Dual PLA presets that
re-exposed the library's PolyTerra PLA (alias rename) get family mints while
'PolyTerra PLA @0.2 nozzle' keeps the library id via inheritance. Fiberon
families keep their authentic byte-copied Bambu catalog ids (sanctioned
multi-vendor brand sharing, frozen in the snapshot).

FlyingBear (8): every preset carries an own-key Bambu generic id; owners
keep theirs, 15 impostor families (S1/Ghost7/Hyper lines) re-minted
family-atomically - '@S1' and '@Ghost7' siblings move together (20 edits).

Sovol (6): the GFL99 copy-paste epidemic. The five same-alias
'Generic * @Sovol SV08 MAX' tunings just DROP their wrong own-id lines so
the library family ids flow through inheritance (alias-shadow-verified);
Generic PLA Silk / SUNLU PETG adopt their library family ids as own keys
(their inherits point at other families, so deletion would collide); the 21
dedicated Sovol/Polymaker families get fresh mints (23 edits).
'Sovol SV07 PLA' keeps GFL99 unambiguously (sole claimant on SV07), frozen
in the snapshot.

Verified: config-equivalence gate zero unexpected diffs; extra check exit 0;
--check exit 0; validator -l 2 exit 0; extended -f -v Snapmaker/FlyingBear/
Sovol all exit 0.
2026-07-03 00:14:27 +08:00
SoftFever
23b0aabb35 Cubicon, Dremel, Anycubic, Artillery, InfiMech, Creality: family filament_ids
Phase 2 long-tail migrations (56 collision groups across six vendors), zero
effective-config change except where stated:

Cubicon (17): 'Cubicon PC @base' verbatim-copied PA-CF's P510cfd0 at
introduction (90a6c53ad5; PC was the config-light stub) - the PC family gets
a fresh mint via its root. The other groups were all one shape: nine @base
presets instantiated on exactly the two printers their dedicated variants
cover with byte-identical passthrough configs, so no compatible_printers
trim exists that CI would accept (instantiated presets must claim a
printer). Fix: flip the nine @bases to instantiation:false and drop their
setting_ids (bases carry none by convention). Every printer keeps an
identical-config selectable preset per family - verified programmatically
before flipping; no machine default_materials references an @base name.

Dremel (3): 'Dremel Generic PLA' is deliberately selectable alongside its
per-printer variants (#6837 re-exposed it), and the variants carry real
overrides - a genuine AMS ambiguity with no config-neutral trim. The three
variants share the fresh family mint OFUYjPc4 as own keys; the root keeps
GFL99. A deliberate same-family id split (P8 shape): Dremel has no device
ecosystem consuming filament_id, and every alternative is a user-visible
regression of #6837. default_materials resolve unchanged.

Anycubic (14): copy-paste/generic-riding across three id eras; 2022-era
Anycubic Generic ABS/PETG/PLA/TPU keep the catalog ids, 13 families
re-minted atomically (72 edits incl. 48 forced same-family re-ids), retiring
the invented GFABS/GFL92/GFL93/GFPLA* space-containing ids.

Artillery (8): GFL99 umbrella + P-hex copy-pastes; Artillery Generic PLA
keeps GFL99, Artillery PLA keeps Pfcf9c4c, 17 families minted (52 edits),
P284941e retired.

InfiMech (14): pure verbatim Bambu-generic copy-paste; the six InfiMech
Generic owners keep their ids, 15 impostor families minted (34 edits).

Creality (8): HF generics copy-pasted GFL99 + id-less sub-brand lines riding
keeper families; owners keep their git-verified ids, 8 families minted (17
edits, own-key layout for members inheriting heterogeneous per-series
parents).

Verified per batch: config-equivalence gate zero unexpected diffs (the nine
Cubicon @base instantiation flips are the only preset-set changes, reviewed
above); orca_extra_profile_check.py exit 0; assign_filament_ids.py --check
exit 0; validator -l 2 exit 0; extended -f exit 0 for all six vendors
(-v Cubicon/Dremel/Anycubic/Artillery/InfiMech/Creality); 14 fully-vanished
ids appended to the retirement ledger.
2026-07-03 00:13:24 +08:00
SoftFever
095fd9fba4 Prusa: move filament_ids off shared templates onto family roots
Phase 2 of the filament_id cleanup (31 collision groups, all one mechanism:
material-class ids on shared fdm_filament_* templates collapsing HF/-CF/
Prusament lines onto one id, plus 3 overclaims). 18 new @base family roots
(owner generic keeps the legacy id, 12 impostor families get deterministic
mints), 72 config-neutral inherits repoints, 6 template filament_id keys
removed (only where every id-less inheritor is covered by a new root;
fdm_filament_asa/pc keep theirs for the frozen Prusament @XL riders),
3 compatible_printers trims on MINIIS printers covered by dedicated @MINIIS
variants. Frozen name-shaped and _NN per-variant ids stay byte-identical.

Verified: config-equivalence gate zero unexpected diffs; extra check exit 0;
--check exit 0; validator -l 2 exit 0; extended -f -v Prusa exit 0 (was 31
groups / 1035 printer-level errors on the pre-apply copy).
2026-07-03 00:09:05 +08:00
SoftFever
674344b3c1 Elegoo: per-product-line roots and filament_ids
Phase 2 of the filament_id cleanup (37 collision groups; one systematic
mistake: every commercial line inherits the material-class @base and its
E<MAT>B00 id). 23 new per-line @base roots with fresh deterministic mints
(no per-line roots existed, contrary to the plan text), 155 inherits
repoints, 23 index entries inserted between the parent @base and the line's
members (load-bearing order), 6 in-place mints on single-member generic
derivatives.

The @base-owning families keep their ids (Elegoo PLA=EPLAB00 etc.); 2 forced
family-atomic re-ids outside collision groups (Elegoo Rapid TPU 95A @EN2
Series/@Elegoo Giga follow their family). Residual 2-family shares
(GPETGB00/GASAB00 with the Centauri -CF lines, pure-template ETPUB00/
EPAHTB00) are printer-disjoint, validator-clean, and frozen in the snapshot.

Verified: config-equivalence gate zero unexpected diffs; extra check exit 0;
--check exit 0; validator -l 2 exit 0; extended -f -v Elegoo exit 0 (was 37
groups / 1332 printer-level errors on the pre-apply copy).
2026-07-03 00:08:08 +08:00
SoftFever
4fc964d293 Flashforge: re-mint umbrella and OFL-riding filament_ids per family
Phase 2 of the filament_id cleanup (69 within-vendor collision groups plus
the 9 live library-cross groups on Creator 5/5 Pro). 507 filament_id edits +
12 compatible_printers trims (G3U 0.6/0.8 bases and HS bases over-claiming
printers covered by dedicated variants):

- The FFG01 umbrella (~140 presets across many families) is broken up: the
  git-verified introducing family (Flashforge Generic PLA) keeps FFG01, and
  83 other families get fresh deterministic mints; the GFB99/GFG99/GFL99
  G3U-era block is resolved the same way.
- Flashforge-branded presets riding OrcaFilamentLibrary generic ids
  (OGFB99/OGFG99/OGFL98/OGFL99/OGFN96/OGFN98/OGFS98/OGFS99/OGFU99) get their
  own family mints; true generic tunings (Generic PLA / PLA Silk / BVOH)
  keep the library id with the library base name (alias-shadow-consistent).
- Layout deviation, signed off: minted ids are declared as own keys on every
  instantiated member instead of new @base roots, because these families
  inherit up to six heterogeneous per-era umbrella parents and
  config-preserving roots would add ~80 artificial files. The snapshot
  ledger records the resulting instantiated_with_id/id_overrides growth,
  which is exactly the ratchet mechanism for keeping such shapes visible.

Verified: config-equivalence gate zero unexpected diffs; extra check exit 0;
--check exit 0; validator -l 2 exit 0; extended -f -v Flashforge exit 0
(was 69 groups + 9 library-cross).
2026-07-03 00:07:30 +08:00
SoftFever
516267d869 Qidi: re-mint copy-pasted filament_ids per material family
Phase 2 of the filament_id cleanup, worst vendor first (97 collision groups,
the GFB99/GFG99/GFL99 copy-paste epidemic). 435 in-place filament_id edits +
5 compatible_printers trims (broader 0.2-nozzle PETG presets over-claiming
Qidi Q1 Pro 0.2 nozzle, covered by dedicated variants):

- The Bambu-catalog generic owners keep their ids byte-identical (Qidi
  Generic ABS/PETG/PLA/TPU/PA/PA-CF/PLA-CF); 6 stray members inside owner
  families are aligned to their family id.
- 64 brand/product-line families (Bambu/HATCHBOX/Overture/PolyLite/Tinmorry,
  QIDI-brand lines, PLA+/High Speed/TPU-95A) get fresh deterministic mints,
  the same id landing in every per-series root of a family
  (@Q2-Series/@Q2C-Series/@X-Max 4-Series bases are multi-root families).
- QD_* device-protocol ids untouched (frozen contract with QidiPrinterAgent).

Verified: config-equivalence gate zero unexpected diffs (5892 presets);
orca_extra_profile_check.py exit 0; assign_filament_ids.py --check exit 0;
validator -l 2 exit 0; extended -f -v Qidi exit 0 (was 97 groups);
-r -v Qidi exit 0 (all references still resolve).
2026-07-03 00:06:38 +08:00
SoftFever
ab60229311 OrcaFilamentLibrary: give every material family its own filament_id
Phase 1 of the filament_id cleanup (filament_id_plan.md section 5): resolve the
16 library-internal ids that covered several different materials at once and
were visible on every printer of every vendor (empty compatible_printers =
compatible with all).

- 7 existing @base roots get their copy-pasted id replaced with a fresh
  deterministic mint (Elas PLA/ASA copy-pastes of Bambu-mirror ids, eSUN
  copy-pastes incl. the OGFL06 eSUN PLA-Marble / Fiberon PETG-ESD polymer
  mismatch).
- 30 rootless families (23 Elegoo product lines riding the material-class
  E*B00 ids, Generic PETG HF / PETG-CF / PP-CF / PP-GF / PE-CF / PLA Matte
  riding their parent generic's template id, PolyLite Dual PLA) each get a new
  config-equivalent @base root carrying the minted id, inheriting the family's
  previous parent; members re-pointed; index entries inserted before the
  @System entries (loader's filament_id map is built in file order).
- Keepers by precedence stay byte-identical: Bambu-catalog mirrors
  (OGFA00/OGFB01/OGFG00, PolyLite/Overture/Fiberon lines), the OFL generics
  (OGFG99/OGFL99/OGFP97/OGFP99), and the Elegoo root-owning families.

Verified: config-equivalence gate — flattened effective configs of all 5892
instantiated presets are byte-identical to pre-migration except the prescribed
filament_id changes; orca_extra_profile_check.py exit 0;
assign_filament_ids.py --check exit 0; validator -l 2 exit 0 (66 vendors);
extended validator -f -v OrcaFilamentLibrary exit 0 (16 collision ids
resolved) and -f -v BBL exit 0 (all 26 BBL-shared ids are same-material
mirrors, alias-shadowed on every overlapping BBL printer — no BBL file
touched). Snapshot: +37 minted ids, id_overrides +30 (new roots override
their template parents by design).
2026-07-03 00:05:37 +08:00
SoftFever
1482fb81fd filament_id: deterministic mint rule, sanctioned-state ledger, validator hardening
Phase 0 of the filament_id cleanup (filament_id_plan.md):

- scripts/assign_filament_ids.py: mint OF-prefixed 8-char ids as
  uuid5(FILAMENT_ID_NAMESPACE, filament_family/<vendor>/<family>) in the
  base62 derivation of the setting_id precedent; loader-faithful effective-id
  resolver (vendor inherits chain + OrcaFilamentLibrary fallback); CLI:
  default assign run (idempotent no-op on a fully-idded tree),
  --mint Vendor/Family, --update-snapshot, --check.
- scripts/filament_id_snapshot.json: the sanctioned-state ledger (1092 ids,
  1965 family claims over the current tree). The tree must equal it exactly,
  both directions, so every id/claim change lands as a reviewable diff; a PR
  snapshot diff is the maintainer gate. scripts/retired_filament_ids.json is
  the append-only retirement ledger; --update-snapshot refuses to resurrect
  retired ids and to sanction new reserved-namespace claims (GF*/QD_*/P-hex/
  null) for non-owner vendors without --allow-shared-catalog.
- orca_extra_profile_check.py: runs check_filament_ids() tree-wide (format,
  snapshot equality, mint conformance, retired reuse, alias hygiene for tuned
  OFL generics, reserved namespaces, structure ratchet). The pre-existing
  check_filament_id() 8-char rule stays BBL/OFL-scoped: grandfathered longer
  ids exist elsewhere (e.g. Prusa's 36-char name ids) and are frozen via the
  snapshot instead.
- PresetBundle::check_duplicate_filament_subtypes now includes
  OrcaFilamentLibrary presets in every vendor's per-printer duplicate check;
  alias-shadowed library presets are excluded via the existing
  m_excluded_from population (verified live in the validator load path), so
  only genuinely visible duplicates are flagged. AMS tray-id resolution logs
  a warning when a filament_id matches 2+ compatible presets (pick unchanged).
- doc/developer-reference/filament_id.md: the authoring rule; PR template
  gains a no-hand-written-ids checkbox.
- scripts/tests/test_filament_id.py: 46 stdlib-unittest tests (mint vectors,
  resolver semantics, every check firing/silent, ledger round-trips, byte
  preservation, real-tree smoke).

Verified: python -m unittest discover -s scripts/tests (46 OK);
python scripts/orca_extra_profile_check.py exit 0 on the unmigrated tree;
assign run is a no-op; rebuilt OrcaSlicer_profile_validator -l 2 exit 0;
extended -f is strictly additive vs baseline (every baseline group preserved,
all new groups involve library presets, alias exclusion proven by BBL-mirror
absence on BBL printers).
2026-07-02 23:17:15 +08:00
SoftFever
051cdd4560 init 2026-07-02 21:57:56 +08:00
3704 changed files with 31606 additions and 41217 deletions

View File

@@ -20,6 +20,8 @@
> Please describe the tests that you have conducted to verify the changes made in this PR.
-->
- [ ] New filament/material profiles: I wrote **no** `filament_id` key by hand — I ran `python scripts/assign_filament_ids.py` and `--update-snapshot` and committed both diffs (see `doc/developer-reference/filament_id.md`)
<!--
> A guide for users on how to download the artifacts from this PR.
-->

View File

@@ -64,13 +64,14 @@ jobs:
set +e
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -s -l 2 2>&1 | tee ${{ runner.temp }}/validate_slice.log
exit ${PIPESTATUS[0]}
# For now run filament subtype check only for BBL profiles until we fix other vendors' profiles.
- name: validate filament subtype check for BBL profiles
# All vendors' filament_id collisions were fixed (see scripts/filament_id_snapshot.json),
# so the duplicate-filament-subtype check runs tree-wide.
- name: validate filament subtype check
id: validate_filament_subtypes
continue-on-error: true
run: |
set +e
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 -v BBL -f 2>&1 | tee ${{ runner.temp }}/validate_filament_subtypes.log
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 -f 2>&1 | tee ${{ runner.temp }}/validate_filament_subtypes.log
exit ${PIPESTATUS[0]}
- name: validate custom presets
@@ -217,7 +218,7 @@ jobs:
fi
if [ "${{ steps.validate_filament_subtypes.outcome }}" = "failure" ]; then
echo "### BBL Filament Subtype Validation Failed"
echo "### Filament Subtype Validation Failed"
echo ""
echo '```'
head -c 30000 ${{ runner.temp }}/validate_filament_subtypes.log || echo "No output captured"

View File

@@ -343,10 +343,7 @@ else ()
endif ()
if (MSVC)
# /MP only matters for the VS generators, where CMake turns it into the
# MultiProcessorCompilation property. Ninja parallelises on its own, and
# clang-cl warns "argument unused" if the flag reaches it.
if (SLIC3R_MSVC_COMPILE_PARALLEL AND CMAKE_GENERATOR MATCHES "Visual Studio")
if (SLIC3R_MSVC_COMPILE_PARALLEL AND NOT IS_CLANG_CL)
add_compile_options(/MP)
endif ()
# /bigobj (Increase Number of Sections in .Obj file)

View File

@@ -1,40 +0,0 @@
if(CMAKE_VERSION VERSION_LESS 3.22)
set(_assimp_url "https://github.com/assimp/assimp/archive/refs/tags/v5.3.1.tar.gz")
set(_assimp_hash "SHA256=a07666be71afe1ad4bc008c2336b7c688aca391271188eb9108d0c6db1be53f1")
else()
set(_assimp_url "https://github.com/assimp/assimp/archive/refs/tags/v5.4.3.tar.gz")
set(_assimp_hash "SHA256=66dfbaee288f2bc43172440a55d0235dfc7bf885dda6435c038e8000e79582cb")
endif()
# Assimp's bundled zlib (contrib/zlib) is too old to compile against the modern
# macOS SDK: its zutil.h takes the classic-Mac branch under TARGET_OS_MAC and
# does `#define fdopen(fd,mode) NULL`, which then clobbers the SDK's real
# `fdopen` prototype in <stdio.h> and breaks the build. On macOS use the system
# zlib (already found by find_package(ZLIB) in deps-unix-common) instead.
if(APPLE)
set(_assimp_build_zlib "-DASSIMP_BUILD_ZLIB=OFF")
else()
set(_assimp_build_zlib "-DASSIMP_BUILD_ZLIB=ON")
endif()
orcaslicer_add_cmake_project(Assimp
URL ${_assimp_url}
URL_HASH ${_assimp_hash}
CMAKE_ARGS
-DASSIMP_BUILD_TESTS=OFF
-DASSIMP_BUILD_SAMPLES=OFF
-DASSIMP_BUILD_ASSIMP_TOOLS=OFF
-DASSIMP_INSTALL_PDB=OFF
-DASSIMP_NO_EXPORT=ON
-DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT=OFF
-DASSIMP_BUILD_GLTF_IMPORTER=ON
-DASSIMP_BUILD_OBJ_IMPORTER=ON
-DASSIMP_BUILD_FBX_IMPORTER=ON
${_assimp_build_zlib}
-DASSIMP_WARNINGS_AS_ERRORS=OFF
-DBUILD_WITH_STATIC_CRT=OFF
)
if (MSVC)
add_debug_dep(dep_Assimp)
endif ()

4
deps/CMakeLists.txt vendored
View File

@@ -367,9 +367,6 @@ include(libnoise/libnoise.cmake)
include(Draco/Draco.cmake)
# Assimp: glTF/GLB/FBX import for the texture-to-color feature.
include(Assimp/Assimp.cmake)
# I *think* 1.1 is used for *just* md5 hashing?
# 3.1 has everything in the right place, but the md5 funcs used are deprecated
@@ -451,7 +448,6 @@ set(_dep_list
dep_libnoise
dep_python3
dep_wxInspector
dep_Assimp
)
if (MSVC)

View File

@@ -0,0 +1,211 @@
# Filament IDs (`filament_id`)
`filament_id` identifies a **material family**: one commercial product line = one id, shared by
all of that material's per-printer / per-nozzle variants. Devices use it to match a physical
spool or tray to a filament preset. It is never per-color, per-printer, per-nozzle, or
per-preset (per-preset identity is `setting_id`).
This page is the rule for authoring `filament_id` in system profiles
(`resources/profiles/**`). CI enforces everything below; the short version is:
> [!IMPORTANT]
> **Never write a `filament_id` value by hand.** New families get their id from
> `python scripts/assign_filament_ids.py`; existing families already have one — inherit it.
## Who consumes the id
Every device integration funnels a tray material id (`tray_info_idx`) through the same
matching pipeline (`PresetBundle::sync_ams_list` and friends):
| Ecosystem | Where the id comes from |
| --- | --- |
| Bambu AMS | device side (RFID / user tray setting) — the `GF*` catalog |
| Qidi box | built from device enums (`QD_*`); needs an exactly matching visible preset |
| Creality CFS | runtime brand/type scoring returns the current preset's id |
| Klipper (AFC / Happy Hare) | runtime lookup by filament type |
| Snapmaker | runtime color/vendor/type match |
Tray-to-preset matching is printer-scoped, but **several consumers match globally by id alone,
first hit wins**: tray display names, `filament_is_support`, vitrification warnings, and
multi-nozzle filament grouping in the slicing pipeline. Two *different* materials sharing one
id feed wrong data to those consumers even when the presets live in different vendors — so
cross-material id sharing is never safe. Within one printer, duplicate ids silently break AMS
matching (first match wins, the tray-edit dialog hides the second preset); the profile
validator's `-f` check rejects this.
## Do I need a new id? The one-question test
> **Would a user consider this a different spool product than anything already in the tree?**
Different polymer, different sub-brand (Basic / Matte / Silk / HF), fiber-filled sibling, or a
second selectable diameter → **new family, new id**. The same spool tuned for another printer
or nozzle → **join the existing family** (inherit its `@base`, write no id key). Tuning a
generic material → **join the OrcaFilamentLibrary family** (inherit `Generic X @System`, keep
the `Generic X` base name, write no id key).
| Situation | id |
| --- | --- |
| Per-printer / per-nozzle variant of an existing material | same id (inherit, never write the key) |
| Sub-brand or product line (PLA vs PLA Matte vs PLA Silk vs PLA HF) | new id each |
| Color | never a new id |
| Second diameter selectable on the same printer (1.75 + 2.85) | sibling family, new id |
| "High-speed" tuned for a *different printer model* | same id (it is a printer variant) |
| "High-speed" selectable *alongside* the normal preset on one printer | new id (it is a product line) |
## Structure rules
1. **Only family roots carry the key.** Root presets (any preset *not* marked
`"instantiation": "true"`, typically `<Family> @base` with `"instantiation": "false"`)
declare `filament_id`; instantiated variants inherit a root and never
write the key. A family may have several roots (e.g. per-series bases) — all of them must
declare the *identical* id.
2. **The family name is the base name**: the preset name with everything from the first
(optionally space-preceded) `@` stripped. `MyBrand PLA @Orca 3D Fuse1` and `MyBrand PLA@HS`
both belong to family `MyBrand PLA`.
3. **Within a family, variants' `compatible_printers` are pairwise disjoint** — per printer
preset, at most one compatible instantiated preset per id. The C++ validator (`-f`)
enforces this.
4. **Generics belong to OrcaFilamentLibrary.** A vendor tuning a generic material inherits
`Generic X @System`, keeps the `Generic X` base name (that alias is what hides the library
preset on your printers), sets a non-empty `compatible_printers`, and writes no id key.
A vendor-*branded* filament never rides a generic family id.
5. **Ids follow the product identity.** The id is a pure function of the product triple
`(filament_vendor, filament_type, family name)` — correcting any of them re-mints the id
**by design**, and `--update-snapshot` records the old id in the shipped succession ledger
with its successor so device trays, calibration records, and user presets keep resolving
(`renamed_from` still gates preset-*name* compatibility as before). A shipped id is never
recycled for a different material: retired ids are blocked forever.
## Minting — nobody invents ids
New ids are deterministic, computed exactly like the `setting_id` precedent
(`scripts/assign_vendor_setting_ids.py`):
```text
FILAMENT_ID_NAMESPACE = uuid5(setting-id NAMESPACE, "filament_id")
= c4d3ff49-4c32-5534-a3e3-00894157ab97
filament_id = "OF" + base62_6( uuid5(FILAMENT_ID_NAMESPACE,
"filament_product/<filament_vendor>/<filament_type>/<family_name>") )
```
`base62_6` is the low 6 base62 digits (alphabet `0-9A-Za-z`) of the UUID taken as a big-endian
integer, most-significant digit first — 8 chars total, within the AMS length limit. The triple
comes from the family root's *flattened* config: `<filament_vendor>` is the filament
**manufacturer** (`"Polymaker"`, or `"Generic"` for generics — never the printer brand),
`<filament_type>` the material type, `<family_name>` the root's base name; the two config
values are inheritable list options and the first element counts. The key contains no bundle
name, so the same product mints the same id in every bundle — hoisting a family into
OrcaFilamentLibrary never changes its id. On the rare collision with any existing or retired
id, the minter salts the input (`…/1`, `…/2`, …) until free and the result is frozen in the
file. Example: `Polymaker/PLA/PolyLite PLA` mints `OF5CgdDq`.
Workflow for a new family:
```bash
# 1. Author the family with NO filament_id key anywhere.
python scripts/assign_filament_ids.py # 2. mint + insert ids into the family root(s)
python scripts/assign_filament_ids.py --update-snapshot # 3. record the new claims in the ledger
python scripts/assign_filament_ids.py --check # 4. verify — the same checks CI runs
# 5. Commit the profile edits together with scripts/filament_id_snapshot.json.
```
`--mint "filament_vendor/filament_type/family_name"` prints the id a triple would mint without
touching anything. The default run is idempotent and never rewrites a valid existing id.
Maintenance modes (normally only used by id migrations): `--remint VENDOR` re-derives a
vendor's declared ids from their triples (a declaration already equal to a salt iteration
of its own triple is conformant and left alone — deliberate salt splits keeping two
presets of one product apart for per-printer AMS matching survive),
`--drop-redundant-ids VENDOR` deletes declarations
that merely re-declare an inherited OFL id, `--add-hint "OLD=NEW"` records a cross-island
succession hint, and `--retire "OLD=NEW"` records succession for a shipped non-island id
that vanished while another declarer kept it alive (lineage the automatic claim vote can no
longer see).
If you skip the tooling, CI fails and prints the remedy: the expected id for your family, and
the instruction to run `python scripts/assign_filament_ids.py --update-snapshot` and commit
the resulting diff.
## Reserved namespaces — never mint or hand-write into
| Space | Owner | Rule |
| --- | --- | --- |
| `GF*` | Bambu AMS/RFID catalog | BBL vendor only; byte-copies elsewhere only where the snapshot already sanctions them |
| `QD_*` | Qidi device protocol | frozen device contract; Qidi vendor only |
| `P` + 7 hex chars (case-insensitive), `"null"` | user-created custom filaments (`CreatePresetsDialog.cpp`) | never appears in system profiles |
| every already-shipped id | grandfather snapshot | frozen as-is; new claims need maintainer sign-off |
| every retired id | `resources/profiles/retired_filament_ids.json` | never used again, for anything |
## The succession ledger
`resources/profiles/retired_filament_ids.json` ships with the app. Each retired id maps to
`{"claims": [...], "successor": <id|null>}` — successor chains are followed to the live end —
and a `hints` map carries the same forwarding for ids Orca cannot retire because another
island owns them (e.g. a `GF*` id whose material also exists as an OFL family). The client
consults the ledger **only on resolution miss** (AMS tray sync, tray-id type lookup,
calibration history, filament-id preset lookup): a live preset always wins first, so BBL
installs resolve `GF*` natively and behavior is unchanged wherever the raw id still exists.
This is what makes identity-driven re-mints (structure rule 5) safe: the old id keeps
resolving to the family's current preset instead of degrading to a `Generic <type>` fallback.
The file is append-only and maintained exclusively by `--update-snapshot` / `--add-hint` /
`--retire`.
## How CI enforces this
Profile CI (`check_profiles.yml``scripts/orca_extra_profile_check.py`) runs
`check_filament_ids()` tree-wide. Its ground truth is
**`scripts/filament_id_snapshot.json` — the sanctioned state**: the id state derived from the
tree must equal the snapshot exactly, in both directions. Any change to the id landscape
therefore surfaces as a diff to that file, and **that snapshot diff is what maintainers review
and gate in a PR**. Never edit the snapshot by hand — `--update-snapshot` regenerates it
deterministically (running it twice changes nothing).
The checks, in brief:
- **Format** — every id is either in the snapshot, `OF` + 6 base62 chars, BBL's, or Qidi `QD_*`.
- **Snapshot equality** — tree claims == snapshot claims **and** tree triples == snapshot
triples, both directions: any `filament_vendor`/`filament_type`/family-name change surfaces
as a snapshot diff.
- **Mint conformance** — a non-grandfathered `OF*` id must equal the mint (or a salt
iteration) of its declarer's product triple; the error prints the expected id.
- **Retired reuse** — any tree id present in `resources/profiles/retired_filament_ids.json` is an error.
Ids that fully vanish from the tree are appended there by `--update-snapshot`; the file is
**append-only**.
- **Alias hygiene** — any vendor preset riding an OFL family id must keep the library
preset's base name, a non-empty `compatible_printers`, and no own id key (structure rule 4);
the error names the rename as the cause.
- **Triple integrity** — every declarer outside the BBL/`QD_*` islands must resolve a
non-empty `filament_vendor` and `filament_type` (generics use `"Generic"`), and all
declarers of one family within a bundle must agree on the triple.
- **Succession integrity** — retired successor chains terminate at a live id (or null) with
no cycles; `hints` keys are live, island-owned ids.
- **Reserved namespaces** — `GF*` outside BBL, `QD_*` outside Qidi, `P<7-hex>` or `null`
anywhere, unless that exact claim is grandfathered in the snapshot.
- **Structure** — no `filament_id` key on instantiated presets; no declared-vs-inherited id
drift; every instantiated system filament must resolve an effective id through its
`inherits` chain (an id-less one is a hard load error in C++ that discards the whole vendor
bundle).
Sharing a **reserved-catalog** id with a new family or vendor (e.g. shipping a Bambu-cataloged
product under another vendor with its authentic `GF*` id) is refused by `--update-snapshot`
unless you pass `--allow-shared-catalog` — and it still lands in the snapshot diff for
maintainer review. Any other new sharing of an existing id is caught by the mint-conformance
check instead.
## FAQ
- **A new color of an existing product?** Never a new id — colors are not families.
- **A second diameter (1.75 mm and 2.85 mm) of the same product?** A sibling family with its
own id: two diameters are separately selectable spool products.
- **A high-speed tune of an existing material for another printer model?** Same family:
inherit the family's root, write no id key.
- **A tuned generic ("our profile for Generic PLA")?** Inherit `Generic PLA @System`, keep the
`Generic PLA` base name, set `compatible_printers`, write no id key.
- **I need to rename a family (or fix its `filament_vendor`/`filament_type`).** Add
`renamed_from` for the name, run `--remint <Vendor>` then `--update-snapshot`: the id
re-derives from the corrected identity and the old id lands in the succession ledger
pointing at the new one. Commit the profile, snapshot, and ledger diffs together.
- **CI says my family needs an id.** Run `python scripts/assign_filament_ids.py`, then
`--update-snapshot`, and commit both diffs. Do not type an id by hand.
For general profile authoring, see the profile development guide on the
[OrcaSlicer wiki](https://www.orcaslicer.com/wiki).

1029
filament_id_plan.html Normal file

File diff suppressed because it is too large Load Diff

397
filament_id_plan.md Normal file
View File

@@ -0,0 +1,397 @@
# filament_id: generation rule + system-profile fix plan
Follow-up to PR #14459 (commit `c2e91cb8`, validator `-f` / `check_duplicate_filament_subtypes`).
Goal 1: a filament_id generation rule for all vendors. Goal 2: an error-pattern taxonomy and a
migration plan that removes every ambiguous filament_id, without touching Bambu (BBL) profiles.
All numbers below are reproducible: a loader-faithful audit script re-derives the validator's
output **exactly** (1256/1256 printer-level errors, 356 logical collision groups, 30 vendors).
Every code claim was verified against source with `file:line` references.
---
## 0. Executive summary
- `filament_id` is a **material-family id**: one id per commercial product line, shared by all
of that material's per-printer/per-nozzle variants. Matching is always `(filament_id +
printer compatibility)`; the invariant from PR #14459 is *per printer preset, at most one
compatible instantiated filament preset per id*.
- Every modern device ecosystem funnels through this id — not just Bambu AMS: Qidi box,
Creality CFS, Klipper AFC/Happy Hare, Snapmaker all emit/consume `tray_info_idx`
(see §1). Several consumers match **globally, without printer scoping**, so two *different
materials* sharing one id is unsafe even across vendors.
- **Proposed rule (§3):** deterministic, script-minted ids — `OF` + 6 base62 chars from
`uuid5(vendor + family)`, declared **only on family-root (`@base`) presets**; variants inherit.
Nobody ever invents an id by hand; CI prints the expected id when one is missing. Existing
unambiguous ids are grandfathered and frozen; `GF*` (Bambu), `QD_*` (Qidi device protocol),
and `P<7-hex>` (user custom presets) are reserved namespaces that must never be minted into.
This was selected by an adversarial design/judge process over a registry-grammar alternative
and a structure-only alternative, then stress-tested; amendments from that stress test are
folded in below.
- **Migration (§5):** fresh-never-reused ids only, family-atomic, names never changed. This is
provably safe: user presets re-derive `filament_id` from their parent on every load
(`Preset.cpp:1658-1682`), 3mf resolves presets by name+config (`Preset.cpp:2490-2576`), and
Klipper/Creality/Snapmaker derive tray ids at runtime. One PR per vendor; CI's `-f` scope
ratchets per vendor until tree-wide.
---
## 1. How filament_id actually works (verified)
### Consumers and scoping
All device integrations converge on one pipeline: device/agent sets `tray_info_idx`
`DevAmsTray.setting_id``Sidebar::build_filament_ams_list` (`Plater.cpp:3423-3493`) →
`PresetBundle::sync_ams_list` / `get_ams_cobox_infos` (`PresetBundle.cpp:3112-3308`) match it
against filament presets.
| Ecosystem | Where the id comes from | Effect of changing a system id |
|---|---|---|
| BBL AMS | device-side (RFID / user tray setting), `DeviceManager.cpp:3823+` | breaks matching — **frozen by mandate** |
| Qidi box | built from device enums: `"QD_" + series + "_" + vendor + "_" + type_idx`, `QidiPrinterAgent.cpp:146-152`; needs an exactly-matching visible preset | breaks matching — **`QD_*` ids are a frozen device contract** |
| Creality CFS | runtime brand/type scoring returns current preset's id (`CrealityPrintAgent.cpp:46-118`) | invisible |
| Klipper (AFC / Happy Hare) | runtime `filament_id_by_type` (`MoonrakerPrinterAgent.cpp:808,936`) | invisible |
| Snapmaker | runtime color/vendor/type match (`SnapmakerPrinterAgent.cpp:22-64`) | invisible |
Matching is printer-scoped (`is_compatible`) in the AMS sync paths and all printer agents — this
is what makes the per-printer invariant sufficient there. But several consumers match
**globally by id alone**, first match wins:
- `get_filament_by_filament_id("")` — tray display name, `filament_is_support`,
`temperature_vitrification` warnings (`PresetBundle.cpp:690-733`; callers
`DevFilaBlackList.cpp:70`, `Plater.cpp:3453`, `SelectMachine.cpp:3560,4623`). The code
comment at `:695` states the assumption outright: an id maps to ONE material globally.
- `MachineObject::setting_id_to_type` (`DeviceManager.cpp:2538`), calibration-history name
lookup (`CaliHistoryDialog.cpp:62`), custom-filament cloud grouping (`Preset.cpp:2839`).
- The slicing pipeline itself: multi-nozzle filament grouping merges project filaments whose
`(filament_id, color)` match (`FilamentGroup.cpp:513-528` via `ToolOrdering.cpp:1164`).
**Consequence:** within one printer, duplicate ids break AMS matching (silent first-wins,
`find_if` at `PresetBundle.cpp:3132/3233`; the AMS tray-edit dialog even *hides* the second
preset, `AMSMaterialsSetting.cpp:894-897`). Across vendors, the same id on *different
materials* feeds wrong name/type/vitrification data to the global consumers and can merge
different materials into one nozzle group. Same id on the *same* material (e.g. `GFL99` =
Generic PLA in 29 vendors) is comparatively benign — those attributes agree.
### Identity machinery
- **Effective id resolution** (`PresetBundle.cpp:4842-5080`): own `filament_id` key → vendor
`filament_id_maps[inherits]` (file order in the vendor index is load-bearing) →
OrcaFilamentLibrary base-bundle map. An instantiated system filament that resolves *no* id is
a hard load error that discards the whole vendor bundle (`:5072`, throw at `:5141-5147`) —
so "missing id" cannot ship; what looked like 26 id-less Flashforge presets actually resolve
to OFL's `OGFL99`/`OGFG99` through the base-bundle fallback.
- **Two family-identity systems exist**: `filament_id` (device matching) and `alias` (name
before `" @"`). OFL shadowing is keyed on **alias**: `update_library_profile_excluded_from`
(`Preset.cpp:3684-3714`) hides an OFL preset (empty `compatible_printers` = compatible with
everything, `Preset.cpp:837`) on printers claimed by a same-alias vendor preset. There is
**no id-based shadowing**. A vendor preset that tunes an OFL generic but renames it
re-exposes the OFL preset and creates a live duplicate. The rule below aligns the two
systems: one family = one alias = one id.
- **User-custom id space**: user-created filaments get `"P" + md5(name)[0:7]` (8 chars,
`CreatePresetsDialog.cpp:533`), or *reuse a system id* when the base name matches an existing
preset (`:510-528`). `"null"` is used as a sentinel. Root user presets persist their id
forever; inheriting user presets re-derive it on every load.
## 2. The id landscape today
**Bambu's grammar** (derived from all 1970 BBL instantiated presets; BBL is internally clean —
181 id definitions, 0 duplicates):
- Classic `GF<LETTER><NN>`: letter = family (A Bambu-PLA, B ABS/ASA, C PC, G PETG/PCTG,
L third-party+generic PLA, N PA/PPA, P PP/PE, R misc, S support, T PET/PPS, U TPU).
Numbers: 00-49 branded ascending, 50-59 fiber-filled, 60-70 partner block, **95-99 generic
tier descending** (99 = the family's plain generic).
- Brand partners `GF<BRANDCODE><NNN>`: GFPM Polymaker, GFOT Overture, GFSNL SUNLU, GFNMK
Numakers. One id per product line; never per color, never per printer/nozzle/diameter.
- Structural rule: the id lives on the material's `@base`; every variant inherits it.
- Hardcoded in C++: `GFS00/GFS01` support check (`DeviceManager.cpp:4739`), per-family PA
defaults `GFU01/03/04` (`CalibUtils.cpp:54-75`) — `GF*` is Bambu's space, byte-frozen.
**Everything else is ad-hoc, invented by individual contributors and imitated** (full history
in §7): OFL's `O`-prefix mirrors (`OGFA00`; introduced 2025-03-31, commit `8c4a65e3e1`),
Tiertime/Afinia `GFx##_##` per-printer-line suffixes, SeeMeCNC per-nozzle ids, LH `LHF_pla`,
LONGER 11-char pseudo-GF ids, Anycubic ids **with spaces** (`"GFPLA Silk"`), Prusa ids that are
entire preset names (36 chars), and mass copy-paste of `GFL99/GFB99/GFG99` onto everything
(Qidi alone stamped `GFB99` into **317 files** across all materials). The only guidance that
ever existed was "≤ 8 chars" — enforced for BBL only (`orca_extra_profile_check.py:292,320`),
and the (now removed) profile wiki's own examples *taught* id copy-pasting.
**The damage, quantified** (audit reproduces validator 1256/1256):
| Ledger | Count |
|---|---|
| Within-vendor logical collision groups (validator `-f`) | **356** across 30 vendors (1256 printer-level errors) |
| OFL×vendor same-id groups (validator blind spot) | 42 — of which **10 are live** (alias mismatch defeats shadowing); 32 already neutralized by alias shadowing |
| OFL-internal: one id, several materials, visible on every printer | **16 ids** (e.g. `OEPLAB00` = 14 distinct Elegoo PLA products; `OGFL06` = eSUN PLA-Marble *and* Fiberon PETG-ESD) |
| Cross-vendor semantic collisions (same id, different materials) | **67 ids** (e.g. `GFU99` also covers a PEBA; Anycubic minted `GFL95` "Matte" ≠ Bambu `GFL95` "High Speed") |
Worst vendors by groups: Qidi 97, Flashforge 69, Elegoo 37, Prusa 31, Cubicon 17,
Anycubic 14, InfiMech 14, Snapmaker 11, Artillery 8, Creality 8, FlyingBear 8.
---
## 3. The rule (proposal)
Selected by a 3-design / 2-judge adversarial process (deterministic-mint won over
registry-grammar and structure-only on ambiguity-prevention, contributor simplicity, and
enforceability), then hardened by three adversarial review passes. This section is written as
the future authoring doc.
### 3.1 The one-question test
> **Would a user consider this a different spool product than anything already in the tree?**
> Different polymer, different sub-brand (Basic / Matte / Silk / HF), fiber-filled sibling, or
> a second selectable diameter → **new family, new id**. The same spool tuned for another
> printer or nozzle → **join the existing family** (inherit its `@base`, no id key). Tuning a
> generic material → **join the OFL family** (inherit the `Generic X @System` preset, keep the
> `Generic X` base name, add no id).
Same id / new id at a glance:
| Situation | id |
|---|---|
| Per-printer / per-nozzle variant of an existing material | same id (inherit, never write the key) |
| Sub-brand or product line (PLA vs PLA Matte vs PLA Silk vs PLA HF) | new id each |
| Color | never a new id |
| Second diameter selectable on the same printer (1.75 + 2.85) | sibling family, new id |
| "High-speed" tuned for a *different printer model* | same id (it's a printer variant) |
| "High-speed" selectable *alongside* the normal preset on one printer | new id (it's a product line) |
### 3.2 Structure
1. **One family = one root.** Each material family has root preset(s) (`instantiation:false`,
typically `<Family> @base`) and only roots carry the `filament_id` key. Instantiated
variants inherit a root and never write `filament_id`. (A family MAY have several roots —
e.g. Qidi's per-series bases — but they must all declare the *identical* id.)
2. **Family identity is declared, not name-derived.** Default: the family name is the
instantiated presets' base name (name with `/\s?@.*$/` stripped — note *optional* space,
because `Afinia PLA@HS`-style names exist). When vendor naming makes that ambiguous, the
root declares an explicit `"filament_family"` key that overrides derivation; tooling errors
loudly when a root's derived family differs from its children's.
3. **Within a family, variants' `compatible_printers` are pairwise disjoint** — that *is* the
PR #14459 invariant, enforced by the validator.
4. **Generics belong to OFL.** A vendor tuning `Generic PLA` inherits
`Generic PLA @System`, keeps the `Generic PLA` base name/alias (so alias shadowing excludes
the OFL preset on those printers, `Preset.cpp:3684`), sets non-empty `compatible_printers`,
and writes no id. A vendor-*branded* filament never rides a generic family id.
5. **Ids are immutable once shipped.** Renaming a family does not change its id (use
`renamed_from`). No id is ever recycled for a different material — stale ids live on in
user root presets and old 3mfs, and a recycled id would silently match the wrong material.
### 3.3 Minting — nobody invents ids
New family ids are computed, exactly like the `setting_id` precedent
(`scripts/assign_vendor_setting_ids.py` / `Slic3r::generate_preset_setting_id`):
```
filament_id = "OF" + base62_6( uuid5( NAMESPACE, "filament_family/<vendor>/<family>" ) )
```
8 chars total (satisfies the AMS length limit), same base62 derivation and a dedicated
namespace constant. On the astronomically rare collision with an existing id, the minter salts
the input (`.../1`, `/2`, …) until free; the result is simply frozen in the file.
- **Script path:** author commits the family with *no id anywhere*;
`python scripts/assign_filament_ids.py` inserts the minted id into the root(s). Idempotent;
never rewrites a valid existing id. A `--mint "<Vendor>/<Family>"` one-shot prints the id
without touching the tree.
- **No-script path:** CI fails with the exact line to paste:
`family "MyBrand PLA" (vendor X) needs filament_id "OFq3xT9k" in "MyBrand PLA @base.json"`.
**Reserved namespaces — never mint or hand-write into:**
| Space | Owner | Status |
|---|---|---|
| `GF*` | Bambu AMS/RFID catalog | byte-copies of authentic Bambu ids only, and only where a checked-in `shared_catalog` list sanctions the family (BBL bundle; OFL mirrors; byte-matching families in other vendors, e.g. Snapmaker's Fiberon) |
| `QD_*` | Qidi device protocol | frozen; Qidi-only; exempt from family-shape checks |
| `P[0-9A-Fa-f]{7}`, `"null"` | user-created custom filaments (`CreatePresetsDialog.cpp:533`) | never emitted for system presets (reserve case-insensitively) |
| everything already shipped | grandfather snapshot | frozen as-is (§5) |
Trade-off accepted: minted ids are opaque (`OFq3xT9k` carries no "PLA" mnemonic — the family
name in the same file provides that). The judges preferred this over a Bambu-style extended
grammar because it removes the "who allocates the next number" ceremony, cannot race between
concurrent PRs, and needs no registry maintenance. If mnemonic ids are strongly preferred, the
runner-up design (`<NSCODE><FAMLETTER><SEQ>` + registry file) is documented in the workflow
records; everything else in this plan is unchanged under either format.
### 3.4 What CI enforces (all vendors, ratcheted)
Extend `scripts/orca_extra_profile_check.py` (it imports the same mint function; setting_id
precedent) and the C++ validator:
1. **Format**: an id is valid iff `OF[0-9A-Za-z]{6}` **or** in the grandfather snapshot **or**
vendor==BBL **or** `QD_*` in Qidi. No whitespace/ASCII/length checks needed outside the
grandfather set — new ids are minted, and the grandfather set is closed.
2. **Uniqueness ratchet**: id→families multimap computed tree-wide; no id may acquire a family
claim not recorded in the snapshot (snapshot legalizes today's benign `GFL99`-style sharing;
new multi-claims are errors; `shared_catalog` entries are the sanctioned exception).
3. **Structure ratchet**: `filament_id` key only on roots; every instantiated filament must
resolve an effective id via the simulated loader walk; all members of one family resolve the
same id; **a preset may not declare an id different from its inherited effective id** (the
`Generic SBS` drift bug class). Pre-existing violations are snapshot-frozen; new ones error.
4. **Mint conformance**: an id new relative to the snapshot must equal the mint (or a salt
iteration); the error message prints the expected value.
5. **Stability**: an `(id, family)` pair on main may not change or vanish, *following
`renamed_from` chains* (so an honest rename passes), unless listed in a maintainer-gated
migrations file. Retired ids go to an append-only `retired_ids` ledger; a retired id may
never be defined again for any family.
6. **Alias hygiene for tuned generics**: a preset inheriting an OFL `Generic * @System` (with
no own id anywhere in its vendor chain) must keep the OFL base name and have non-empty
`compatible_printers` — error message names the rename as the cause.
7. **C++ validator**: extend `check_duplicate_filament_subtypes` (`PresetBundle.cpp:5654`) to
include OFL presets in every vendor's per-printer check, *minus* alias-excluded ones —
`m_excluded_from` is already populated in the validator context (`update_system_maps` at
`PresetBundle.cpp:2302`), so the 32 shadowed pairs won't false-positive and the 10 live ones
will be caught. Run `-f` per vendor in CI, widening as vendors are cleaned (§5).
8. **Runtime backstop** (one-line change): log a warning when the AMS-sync `find_if`
(`PresetBundle.cpp:3132/3233`) finds 2+ compatible presets for one id — the only layer that
can see side-loaded/forked bundles.
---
## 4. Error-pattern taxonomy (goal 2) — with counts and fixes
356 groups were classified by 37 agents reading the actual profiles, spot-checked
independently (1 substantive disagreement in 14 samples). Counts below fold the spot-check
corrections in. **Fix rule for all patterns: replacement id values are always freshly minted
`OF*`; the "which preset keeps the id" decision uses the precedence _Bambu-catalog material >
OFL generic family > family that historically introduced the id_.**
| # | Pattern | Groups | Fix |
|---|---|---|---|
| P1 | `copy_paste_id` — different materials share an id verbatim (task's error 1). Qidi's `GFB99`×317-files epidemic; Anycubic's three id "eras"; Peopoly `GFSL99` on ABS | ~167 | impostor families get minted ids on their roots; owner keeps the id |
| P2 | `wrong_inherits` / id-less product lines — a variant inherits another family's root (PR #14459's Panchroma case) or a product line never got its own root (Prusa HF; Flashforge's ~140-preset `FFG01` umbrella) | ~87 | create per-family roots with minted ids; re-point `inherits`; **never** delete the id-less shadow/base files — convert them (they carry real config: Flashforge `fdm_filament_pla` differs materially from OFL's) |
| P3 | `generic_family_overlap` — vendor-*branded* preset rides a generic family id via inheritance (task's error 2, generalized). Includes the 10 live OFL duplicates | ~68 | branded presets get minted family ids; true generic tunings instead adopt the OFL family *with matching alias* (rule 3.2.4); Sovol is the elegant case — just **delete** its wrong own-id lines and let OFL ids flow through inheritance |
| P4 | `overclaim_compat` — same material, broader variant claims a printer that a dedicated variant covers (the BBL H2DP pattern fixed in #14459) | ~24 | trim `compatible_printers` of the broader preset (Dremel, Cubicon `@base`s that are also instantiated, Wanhao France Bowden/Direct) |
| P5 | template-carried id — id declared on a shared settings template (`fdm_filament_*`, `fdm_filament_common`) so every family inheriting it collapses (Prusa, Ginger Additive, Snapmaker TPU base) | inside P1/P2 counts | move ids off templates onto family roots |
| P6 | OFL-internal collisions — 16 ids spanning several materials, visible on every printer (Elegoo blocks, Elas `OGFA00`×3, `OGFL06` polymer mismatch, `Generic PETG HF/PETG-CF @System` missing own ids) | 16 ids | fix inside OFL first (it's the base bundle every vendor resolves against) |
| P7 | alias-mismatch re-exposure — vendor tunes a generic under a different name, OFL preset resurfaces (Snapmaker `PolyTerra J1 PLA` vs OFL `PolyTerra PLA`) | 10 live | rename-to-alias where it's genuinely the same family, else mint |
| P8 | per-variant ids — no ambiguity, but family semantics broken: every variant has its own id (Prusa name-ids, SeeMeCNC nozzle suffixes, Afinia/Tiertime `_##`, iQ) so device matching can't identify the material across nozzles | ~210 presets | grandfather (they're unambiguous); converge opportunistically; document as anti-pattern |
| P9 | format violations — spaces (`"GFPLA Silk"`), >8 chars (LONGER, SeeMeCNC, LH), GF-shaped inventions (Anycubic `GFL93-97`, CoLiDo `GFA99`) | in the above | fixed as a byproduct of re-minting; snapshot freezes the unambiguous rest |
| P10 | cross-vendor semantic collisions — 67 ids meaning different materials in different vendors (dangerous via the global unscoped consumers, §1) | 67 ids | mostly eliminated by P1-P3 re-minting; the remaining same-material generic sharing is legalized by the snapshot |
| P11 | deliberate coexistence & data hygiene — Snapmaker "Benchy" demo presets (gated by `compatible_prints`, which the id check can't see) and a self-collision from duplicate `compatible_printers` entries | 4 | give demo presets own minted ids; dedupe list entries; add a lint for duplicate array entries |
New patterns beyond the two in the task (goal 2.3): P5-P11.
---
## 5. Migration plan
### Safety foundation (verified, §1/§7)
Safe: fresh never-used ids, family-consistent; trims of `compatible_printers`; inherits
re-pointing; OFL id changes (children re-derive). Unsafe: touching `BBL`/`QD_*`; recycling or
swapping ids; splitting a family's id; **deleting or renaming preset names** (user presets
whose `inherits` no longer resolves are dropped at load, `Preset.cpp:1687-1691`) — if a name
must go, `renamed_from` coverage is mandatory.
### Phases
0. **Land the rule + tooling first** (no profile changes): `scripts/assign_filament_ids.py`
(mint + insert + `--mint`), the extended `orca_extra_profile_check.py` checks in
snapshot-ratchet mode, the C++ validator OFL cross-check, the runtime warning, the rule doc.
Generate `filament_id_snapshot.json` (id→families multimap over main) and empty
`retired_ids.json` — both checked in.
1. **OFL first** (it's the base bundle every vendor resolves against): fix the 16 internal
collisions (Elas/eSUN/DREMC copy-pastes get mints; `Generic PETG HF/PETG-CF/PP-CF/PP-GF/
PE-CF/PLA Matte @System` get their own family roots+ids instead of collapsing into their
parent generic), keeping every current effective id that is unambiguous.
2. **Per-vendor PRs, worst-first**: Qidi → Flashforge → Elegoo → Prusa → Cubicon → Anycubic →
InfiMech → Snapmaker → the long tail (22 vendors, mostly 1-8 one-line fixes). Each PR flips
that vendor into CI's `-f` scope (`check_profiles.yml` currently `-v BBL -f`; append
vendors as they reach zero; when all are in, drop `-v` and run tree-wide).
3. **Delete the ratchet allowlists** once tree-wide zero holds; checks become hard rules for
everything born after the snapshot.
### Migration-script contract (from the adversarial pass — important)
- Consumes from the classification only: group membership, fix category, keep-id precedence,
inherits-repoint targets. **All replacement id values are recomputed via the mint** — id
literals in analysis notes (e.g. `GFA00_02`, `GFS98`, `GFG96`) are legacy-culture artifacts
and are ignored with a warning; assert no emitted id matches `^(GF|QD_|P[0-9A-Fa-f]{7}$)`.
- **Family-atomic**: re-idding any preset re-ids every same-family sibling in the same commit,
even siblings outside the collision group (FlyingBear `GFB99 @S1` vs `@Ghost7`); Prusa is
family-atomic per material (its HF/CF families span frozen `_N`-suffix ids — freeze what's
unambiguous, mint once per family for the colliding members).
- **Diff bound**: a vendor migration PR may only touch collision-group files + same-family
siblings of re-idded presets; every id that is per-printer-unambiguous today stays
byte-identical.
- **Config-equivalence gate**: dump every instantiated preset's flattened effective config on
main and on the PR head; the diff must be empty except `filament_id`/`inherits`/
`compatible_printers` edits the plan prescribes (this is what makes the Flashforge
shadow-file conversions safe: each id-less `fdm_filament_*` shadow becomes a named vendor
family root carrying its config byte-for-byte, children re-pointed, then the shadow name
retired).
- **No deletions**: redundant presets (Flashforge's byte-identical `Generic X`/`Flashforge X`
twins) are re-minted, not dropped; consolidation with `renamed_from` is a separate,
human-reviewed cleanup.
- Expected user impact: none for inheriting user presets, 3mfs, Klipper/Creality/Snapmaker
sync. Residue: user *root* presets that copied an old system id keep it forever
(AMS auto-match falls back to generic-by-type — low severity, unavoidable from the repo).
### Vendor-specific notes (from classification)
- **Qidi (97)**: three profile generations. `QD_*` generics are correct and frozen; the fix is
the brand families (Bambu/HATCHBOX/Overture/PolyLite/Tinmorry/QIDI-brand) that all carry
`GFB99/GFG99/GFL99`. Multi-root families are the norm (`...@Q2-Series` / `@Q2C-Series` /
`@X-Max 4-Series` bases) — same mint lands in every series root of one family.
- **Flashforge (69)**: two umbrellas (`FFG01` ~140 presets; `GFB99/GFG99/GFL99` G3U-era) +
OFL-riding branded presets + shadow-file conversion (above).
- **Elegoo (37)**: single mistake — every commercial variant inherits the material-class
`@base` (`E<MAT>B00`); mint one id per product line (Silk/Matte/PRO/Rapid/…), roots exist.
- **Prusa (31)**: HF product lines need their own roots; ids move off `fdm_filament_*`
templates; frozen name-shaped and `_N` ids stay.
- **Sovol (6)**: delete the wrong own-id lines; correct ids flow from OFL by inheritance.
- **Cubicon (17)**: `@base` presets are themselves instantiated + over-claiming; 9 file edits.
---
## 6. Concrete work items (PR-sized)
1. `scripts/assign_filament_ids.py` + mint function + tests (incl. the 14-group regression set
from the adversarial pass). *(new)*
2. `orca_extra_profile_check.py`: checks §3.4-1..6 in ratchet mode + snapshot/ledger files;
drop the BBL/OFL-only gate at `:292` and the OFL skip at `:601`. *(extend)*
3. C++ validator: OFL-aware `check_duplicate_filament_subtypes`; runtime ambiguity warning at
the two `find_if` sites. *(small)*
4. Rule documentation: `doc/developer-reference/filament_id.md` (recreate the path; wiki
cross-link) + profile-PR template checkbox ("new materials: no filament_id key anywhere; CI
prints the minted id"). *(new)*
5. OFL migration PR (phase 1).
6. Per-vendor migration PRs (phase 2), each widening CI `-f` scope.
The audit tooling from this analysis (loader-faithful resolver; reproduces the validator
1256/1256) is in this session's scratchpad (`audit_filament_ids.py`) and is the natural seed
for items 1-2.
## 7. Evidence & methodology
- Validator ground truth: `OrcaSlicer_profile_validator -f` tree-wide → 1256 errors; audit
script reproduces exactly (356 logical groups after dedup by (vendor, id, preset-set)).
- Code analysis: 5 parallel agents over runtime consumers, persistence/migration surface,
loader semantics, Bambu grammar, and convention history — all claims carry `file:line`.
- Classification: 37 agents (one per vendor chunk) reading actual profile JSONs; 14-sample
independent re-derivation found 1 substantive error (a Flashforge group mislabeled
`missing_id`; corrected — the loader resolves those ids from OFL).
- Design: 3 independent designs → 2 judges (both chose the deterministic mint; scores 58/50/56
and 60/54/56) → 3 adversarial attackers (35 scenarios; every `breaks` finding is folded into
§3.2-3.4/§5 as an amendment: declared families, multi-root support, ratchet-not-absolute
checks, no-deletion rule, shadow-file conversion, classification-id-literal quarantine,
diameter siblings, case-insensitive P-hex reservation, fork guidance).
- Key history: `OGF*` born 2025-03-31 (`8c4a65e3e1`, no PR); `_##` suffix born PR #9739;
8-char check born PR #9574; wiki (with the id-copy-paste example) removed 2025-11-24
(`f0d79b99eb`).
## 8. Open decisions for maintainers
1. **Id format**: opaque deterministic `OF*` mint (recommended, judges 2/2) vs mnemonic
registry grammar (runner-up). Everything else in the plan is format-agnostic.
2. **Multi-vendor brands** (Snapmaker ships Fiberon with authentic `GF*` ids): sanction via
`shared_catalog` (recommended, low churn) vs hoisting those families into OFL.
3. **Benchy-style demo presets**: mint ids per demo preset (recommended) vs teaching the
validator `compatible_prints` gating.
4. **Alias alignment**: fix the 10 live alias-mismatch OFL duplicates by rename-to-alias
(better long-term, needs `renamed_from`) vs minting vendor ids (safer, more ids).
5. **Where the rule doc lives**: in-repo `doc/` (recommended — CI messages need a stable link)
vs wiki-only.

314
filament_id_plan_v2.md Normal file
View File

@@ -0,0 +1,314 @@
# filament_id plan v2: the `get_filament_id` (P+md5) proposal, validated
> **Status note:** the verdict in §0-§4 and §7-§8 stands. The work items in §5 are
> superseded by `filament_id_plan_v3.md` (OFL-as-catalog, content-addressed triple key,
> succession ledger): W1/W3 are carried into v3.0, W2/W5 carry unchanged, W4 is dropped.
Follow-up to `filament_id_plan.md` (v1, implemented on `feature/filament_id`: deterministic
`OF*` mint + snapshot ledger + 30-vendor migration, tree-wide validator `-f` = 0).
**Proposal under review:** mint system `filament_id`s with the scheme of
`static std::string get_filament_id(std::string vendor_typr_serial)`
(`src/slic3r/GUI/CreatePresetsDialog.cpp:487-552`) — Bambu's user-custom-filament id
allocator — so system ids are "compatible with Bambu AMS sync".
Every claim below was verified in source (`file:line`), against upstream BambuStudio, or in
primary online sources (URLs in §8). Verdict first, evidence after.
---
## 0. Executive summary — the verdict
**Do not re-mint system ids as `P+md5(name)`. Keep the v1 `OF*` mint.** Three facts decide it:
1. **The AMS/device path never validates id *shape*.** `tray_info_idx` is an opaque string
end-to-end: sent verbatim (`DeviceManager.cpp:1642`), parsed verbatim
(`DevFilaSystem.cpp:512-514`), and firmware persists arbitrary bytes — an A1 stored and
echoed a *corrupted* id `Pde2\xea58c` (bambulab/BambuStudio#5436). A P-shaped system id
is not "more acceptable" to the device than an `OF*` one.
2. **P-shape buys zero recognition.** The printer resolves tray ids only against its built-in
`GF*` catalog plus the *account's* cloud custom-filament cache (official Bambu wiki); ids
it cannot resolve round-trip as `?`. Orca can never upload system presets into that cache
(`Preset.cpp:2071` gates cloud upload on `is_user()`), so an Orca system `P` id shows `?`
exactly like an `OF` id. The public catalog id space is 100% `GF*` (ha-bambulab
`filaments.json`: 86/86 ids).
3. **P-shape is the one shape the codebase punishes.** The *only* id-shape dispatch in the
entire tree is `MachineObject::check_ams_filament_valid` + `update_filament_list`
(`DeviceManager.cpp:5335/5352/5395/5410`, gate `setting_id.size()==8 && [0]=='P'`), a
user-custom lifecycle reconciler that **remotely wipes an AMS tray**
(`command_ams_filament_settings(..., "", "", white, "", 0, 0)` at `:5345`) or rewrites its
temps (`:5361-5367`) when a P-shaped tray id drops out of the user-root preset list
(`:5219`, armed at `:5268`). Name-keyed minting makes system id == user-custom id *by
design* (same name → same id), so "user deletes their now-redundant custom after the
system preset ships" becomes a remote tray wipe. `GF*` (wrong length) and `OF*` (wrong
first char) are structurally immune.
The proposal's *real* value lives in two places, and v2 captures both without the re-mint:
- **The adopt step** (the function's first branch, `:509-511/:526-528`): same base name →
same id. In-app, this already gives Orca-local convergence today — a user custom named
like a system family adopts the system id *whatever its shape*. At authoring time, the
same semantics = the v1 `shared_catalog` mechanism; §5/W4 extends it (curated, optional)
to let byte-authentic Bambu-catalog families carry authentic `GF*` ids.
- **The hash step belongs to user space.** `P+md5(name)` is Bambu's allocator for *user*
presets; the community is already asking for it there (OrcaSlicer PR #13315, unique ids
for inherited user presets). §5/W5 endorses that lane. System profiles stay out of the
`P` namespace — and v2 adds client hardening (W1) because ten *already-shipped* system
P-hex ids (Cubicon `P510cf**`×8, Ginger `P510eff9`, Artillery `Pfcf9c4c`) sit inside the
wipe-gate's shape match today.
Judge panel (3 independent lenses over 4 variants; §7): keep-`OF*` won 23/40 aggregate
(device-ecosystem 6, identity-semantics 8, migration-enforceability 9) vs full Bambu
emulation 11, hash-only name-keyed 12, vendor-scoped P-hash 12. The single dissent
(device-ecosystem, favoring adoption) is honored by W4; its own verdict on the shape was
"adopt the semantics, not the costume".
---
## 1. What the proposed generator actually is (verified)
`get_filament_id(vendor_typr_serial)` (`CreatePresetsDialog.cpp:487-552`), called with
`"<vendor> <type> <serial>"` (`:1128`, `PLA-AERO``"PLA Aero"`), and per-preset with the
display base name when cloning presets for a user-created printer
(`Preset.cpp:2785-2803``:2798-2800`):
1. **Adopt:** scan a temp bundle of *every* vendor bundle on disk
(`PresetBundle::load_system_filaments_json`, `PresetBundle.cpp:2342-2391`) plus the
user's presets, then the live bundle (`Preset.cpp:2831-2842`); if any preset's base name
(`name.substr(0, first('@') - 1)`) equals the input and its id ≠ `"null"`, **return that
id** — which can be `GF*`, `QD_*`, `OF*`, or another user's P-hex.
2. **Mint:** `"P" + md5(input)[0:7]`, lowercase hex over the raw UTF-8 bytes (`:533`,
`:480`).
3. **Collide:** if the id is held by a *different* name, re-hash with a local wall-clock
salt (`md5(input + get_curr_time())`, `:547`) — non-deterministic by construction.
Upstream parity: the function and its caller input construction are **byte-identical** in
BambuStudio master (`CreatePresetsDialog.cpp` L434-499 / L1061; diff empty, fetched
2026-07-03). So identical dialog inputs in BBS and Orca yield identical ids — the kernel of
the proposal's compatibility claim.
Reproduction limits (why "exactly the approach" cannot be ported to system minting anyway):
- The base-name parser truncates names lacking a space before `@` (`"Afinia PLA@HS"`
`"Afinia PL"`); 48 shipped presets hit this, and the *clone* path parses differently
(`" @"` two-char, `Preset.cpp:2792`), so the app itself is internally inconsistent.
- 597 presets (361 whole families, e.g. all of Anker) contain no `@` and are invisible to
the adopt scan and its collision map (`:505-508`).
- The collision salt is wall-clock time — unreproducible across machines, so id values
cannot be CI-verified from names; a ledger would still be the sole source of truth.
- Shipped P-hex ids don't round-trip from names today: Cubicon `P510cfb0`
`P+md5("Cubicon ABS")` = `Pc624b68` (verified) — so even Bambu-lineage P ids are not
name-derivable in practice.
## 2. What Bambu AMS sync actually consumes (verified)
- Send: `command_ams_filament_settings` puts the preset's `filament_id` verbatim into
`tray_info_idx` (`DeviceManager.cpp:1642`); cloud vs LAN differ only in transport
(`:2502-2536`). No client- or firmware-side rejection path exists; the ack parse stores
whatever the printer echoes (`:3808-3860`).
- Resolve (slicer side): tray id → first compatible root preset with equal `filament_id`
system *and* user roots, no shape filter (`PresetBundle.cpp:3151`, `:3252-3254`); misses
fall back by `"Generic <type>"` name, then similarity, then keep-previous
(`:3157-3169`, `:3260-3305`). `setting_id_to_type` resolves against `is_system` presets
only (`DeviceManager.cpp:2538-2559`) — a system preset resolves *because it is system*,
never because of its id shape.
- Resolve (device side): built-in `GF*` catalog + the account's cloud custom cache;
custom-filament sync is **cloud-only** (not LAN), and an id the printer has no data for
displays `?` until opened once on the printer screen (official wiki, §8). Third-party
consumers see customs as "unknown" (ha-bambulab #466/#540).
- Since Jan 2025, AMS configuration is gated behind Bambu-signed clients on new firmware
(Developer Mode / older firmware exempt) — any AMS-sync benefit for Orca is conditional
on that regardless of id scheme (Bambu authorization blog, §8).
- Orca already ships non-`GF`/non-`P` shapes to Bambu trays in the field: OFL presets are
compatible with every printer (`PresetBundle.cpp:5681-5684`) and carry `OGF*`/`OFL*` ids
(e.g. `OFLSBS99`, hardcoded in `MoonrakerPrinterAgent.cpp:654`). The premise "the
ecosystem has only ever seen `GF*`/P-hex" is already false, with no observed rejection.
**Cross-client reality check.** The name-keyed benefit ("a BBS-created custom named like an
Orca system preset lands on the same id, so Orca's sync matches the system preset") is
mechanically true but narrow: it needs byte-exact names (including the `PLA Aero` mapping
and single spacing), the unsalted hash path, cloud mode, and permissive firmware. The
symmetric case already works in Orca *without* any re-mint: an Orca user creating a custom
named like a system family **adopts the system id** via the in-app adopt branch, so local
custom ↔ system ↔ tray matching is shape-independent today.
## 3. The disqualifying finding, in mechanism form
`update_filament_list` (`DeviceManager.cpp:5208-5272`) snapshots `{filament_id → temps}`
over **user root presets only** (`preset.is_user() && preset.inherits() == ""`, `:5219`);
ids that vanish or change temps between snapshots are armed into `checked_filament`
(`:5268`). `check_ams_filament_valid` (`:5311-5445`) then, for every AMS/virtual tray whose
`setting_id` matches `size()==8 && [0]=='P'`:
- id armed and **not** in the current user-root list → remotely **clears the tray**
(`:5335-5349`, `:5395-5408`);
- id armed and in the list with unequal temps → **rewrites tray temps from the user
preset**, ignoring same-id system presets (`:5352-5374`; `PresetBundle.cpp:3719` skips
non-user presets; unguarded `find()->second` at `:3717`).
Failure scenario under the proposal: system `"PolyLite PLA"` ships with
`P+md5("PolyLite PLA")`. A user's same-named custom (BBS- or Orca-created — same id by
design) is on an AMS tray. The user deletes the now-redundant custom. Next status refresh:
the id is armed, absent from user roots → Orca wipes the tray, even though the system
preset still resolves that id perfectly. Runs continuously (`StatusPanel.cpp:3275-3277`).
Two corollaries independent of the proposal (→ W1):
- The ten shipped P-hex **system** ids (Cubicon/Ginger/Artillery) already pass the shape
gate; only the "id must transit a user root" arming condition protects them, and the
in-app adopt branch can create exactly that transit today.
- `assert(it->first.size() == 8 && it->first[0] == 'P')` (`:5252`) is violable today: a
user custom named `"Bambu PLA Basic"` adopts `GFA00` onto a user root and aborts debug
builds.
## 4. Identity costs the re-mint would add (quantified, tree dry-run)
- 1148 distinct family base names; **198** appear in 2+ vendors; **159** groups carry ≥2
distinct ids today and would silently merge under name-keying (Generic PETG alone: 15
ids today). 112 involve BBL; 82 are `GF↔OGF` mirror pairs.
- **4** cross-vendor groups share a name across materially different `filament_type`
(Generic PLA Silk, Generic PETG-CF, Generic PA6-CF, Generic PE-CF) — one merged id would
feed wrong type/name/vitrification to the global unscoped consumers
(`PresetBundle.cpp:690-733`; `DeviceManager.cpp:2538`; `FilamentGroup.cpp:513-519`) (→ W3).
- 33 deliberately-split families on the branch would re-merge (25 Elegoo-vs-OFL pairs, 7
FlyingBear-vs-InfiMech "Other *" pairs, the salted Cubicon PC pair) — undoing v1
decisions that the fixture gate forced.
- Renames become id migrations: name-keyed ids re-key on any marketing rename; the ledger
can freeze them, but then name-derivability — the scheme's selling point — dies family
by family. The `OF*` key (`vendor/family`) survives display-name churn.
- Entropy drops 35.7 → 28 bits (still 0 collisions at today's 1148 names; ~0.75 expected
at 20k). Re-mint surface: 391 unshipped ids across 1327 files, 13 hardcoded tests, the
reserved-namespace policy inversion, and a retired-ledger rebuild.
## 5. Work items (delta from `feature/filament_id` HEAD)
**W1 — client hardening (C++, small, ships with the migration PR train).**
a. Skip the tray-reset and temp-rewrite in `check_ams_filament_valid` when **any
`is_system` filament preset carries the tray's id** — no compatibility filter,
mirroring the semantics of the global consumers (`get_filament_by_filament_id`,
`setting_id_to_type`). This protects the ten shipped P-hex system ids and any BBS
custom colliding with them, while ids resolving only to user presets (or to nothing)
keep today's cleanup behavior.
b. Relax the debug assert `DeviceManager.cpp:5252` to a log (user roots legitimately
carry adopted `GF*`/`OF*` ids).
c. Guard `PresetBundle.cpp:3717` (`find()` unchecked before `->second`).
d. Regression-check that AMS sync never clears trays whose id it merely cannot resolve
(OrcaSlicer#4431 class): the keep-previous fallbacks at `PresetBundle.cpp:3163-3169`,
`:3296-3305` must cover the UI paths.
**W2 — tooling + doc guardrails.**
a. Document `^OF[0-9A-Za-z]{6}$` as the system-mint namespace. **Do not add it to
`reserved_space_owner`** (`scripts/assign_filament_ids.py:394-402`): check 6 exempts
only snapshot-grandfathered claims and the `--update-snapshot` refusal gate
(`:596-621`) would then reject every *future* mint. The space is already enforced —
check 3 (mint conformance, `:488-509`) errors on any OF-shaped id that does not equal
its own family's mint (covered by `test_check3_of_id_must_match_mint`), and the
snapshot diff is the human gate. Add one doc paragraph + a test asserting a foreign
vendor claiming another family's OF id fails check 3.
b. `doc/developer-reference/filament_id.md`: add a "why not P-hex" section citing the
`DeviceManager.cpp:5335` gate and the account-cache resolution model — community
pressure toward `P+md5` exists (PR #13315) and will recur.
c. Document that user roots may legitimately hold adopted system ids (incl. what that
means for cloud upload, `Preset.cpp:1874-1876`), matching BBS behavior.
**W3 — data hygiene: the 4 same-name-different-type groups.** Audit each (they confuse
name-based fallback matching and any future interop even under vendor-scoped ids):
verify whether the divergent `filament_type` is a data bug (e.g. OFL `Generic PETG-CF`
resolving `PETG`, OFL `Generic PE-CF` resolving `PE`, Flashforge `Generic PLA Silk`
mixing `SILK`+`PLA`, Creality-vs-Elegoo `Generic PA6-CF` as `PA-CF`/`PA6-CF`) or
intentional; fix by type alignment or rename with evidence, gated by
config-equivalence (only `filament_type` diffs as prescribed) + full validator suite.
**W4 — optional, curated GF adoption (the proposal's adopt step, done safely).** Extend the
v1 `--allow-shared-catalog` mechanism into an explicit per-family adoption worksheet: a
family may carry a byte-authentic Bambu catalog id iff (i) it is verifiably the same
commercial product as the BBL family (vendor evidence + equal `filament_type`), (ii)
validator `-f` stays 0 tree-wide (alias shadowing covers BBL/OFL overlaps), (iii) the
fixture gate passes, (iv) the snapshot diff records the adoption. Candidates: the 13
actionable BBL-overlapping families minted `OF*` in v1 (e.g. Qidi `Bambu ABS``GFB00`,
`PolyLite PLA``GFL00`, `Overture PLA``GFL04`, `PolyLite ABS``GFB60`; `OFLSBS99` is
shipped and frozen; the two type-hazard OFL families are excluded until W3 lands).
**Honest benefit statement:** these presets are compatible only with non-Bambu printers,
so no Bambu device resolves their ids today; the payoff is one-product-one-id coherence
for the global consumers, project portability, and readiness for W6. Recommended seed:
the four Qidi brand families; generics deferred.
**W5 — user-preset lane (where the proposal's hash belongs).** Support unique ids for
inherited user presets (the PR #13315 pain: children share the parent's id, so AMS always
resolves the generic parent). `P + md5(preset name)` matching
`CreatePresetsDialog::get_filament_id` is correct *in user space*: adopt-first against
existing ids, then hash; ids frozen per preset after mint. Constraints: never emitted
into system profiles (W2a makes that mechanical); W1 must land first so shared-id
lifecycles can't wipe trays.
**W6 — recorded future option:** SoftFever's "upload OrcaSlicer's filament database
(especially the filament ID) to the printer" (PR #12724 comment). If pursued, Orca ids
become first-class device-visible artifacts and the P-mimicry question is permanently
moot; the `OF*` scheme is the *better* citizen there (collision-free vendor-scoped ids,
no user-custom masquerade).
Unchanged from v1: mint rule, snapshot/retired ledgers (no OF retirement — nothing is
re-minted), CI wiring, migration commits. `filament_id_plan.md` remains the implemented
baseline; this document records the v2 decision and its follow-on work.
## 6. Gates (per work item)
Same battery as v1, all local: `python scripts/orca_extra_profile_check.py` exit 0;
`assign_filament_ids.py --check` exit 0 (46+ unit tests green; new tests for W2a
reservation and, when implemented, W4 adoption sanctioning); validator `-l 2` exit 0,
`-f` tree-wide exit 0, `-r` BBL+Qidi exit 0; custom-preset fixture overlays (the W1/W4
changes touch exactly the preset-visibility machinery the fixtures exist to protect);
config-equivalence: flattened effective configs differ only in prescribed keys
(`filament_id` for W4, `filament_type` for W3, none for W1/W2). W1 additionally needs a
manual AMS smoke test on a live Bambu printer (tray set/clear round-trip) — a release
checklist item, not CI (the workflows have no hardware runners).
## 7. Scheme comparison (judge panel record)
| Variant | Device lens | Identity lens | Migration lens | Σ |
|---|---|---|---|---|
| V1 full Bambu emulation (adopt incl. `GF*` + P+md5 + salt) | 7 | 2 | 2 | 11 |
| V2 hash-only name-keyed `P+md5(base name)` | 4 | 4 | 4 | 12 |
| V3 vendor-scoped P-hash (`P+md5(vendor/family)`) | 2 | 5 | 5 | 12 |
| **V4 keep `OF*` uuid5 vendor-scoped (chosen)** | 6 | 8 | 9 | **23** |
All three judges answered the pivotal question the same way: the P-hex **shape** confers no
device/cloud benefit (resolution is by value against catalog + account cache; the path is
otherwise shape-agnostic) and is the only shape with a destructive client-side gate. V3 is
strictly dominated (all of the re-mint cost, none of the name-keyed benefit). V1's genuine
half — GF adoption — is captured by W4 at zero re-mint cost.
## 8. Evidence index
Code (this branch): `CreatePresetsDialog.cpp:487-552,1128,2798-2800` (generator + callers);
`DeviceManager.cpp:1642` (verbatim send), `:2538-2559` (system-only type resolve),
`:5208-5272` (user-root snapshot, `:5219`, `:5252` assert, `:5268` arming), `:5311-5445`
(P-shape gate + tray wipe `:5335/:5345`, temp rewrite `:5352-5374`, virtual tray
`:5395-5435`); `DevFilaSystem.cpp:512-524`; `PresetBundle.cpp:690-733,3116-3128,3151,
3252-3254,3709-3767,5674-5753`; `Preset.cpp:1874-1876,2071,2785-2842`;
`AMSMaterialsSetting.cpp:886,894-897`; `WebGuideDialog.cpp:67`;
`PresetComboBoxes.cpp:1952-1966`; `MoonrakerPrinterAgent.cpp:654`;
`scripts/assign_filament_ids.py:69,71-72,118-144,394-402,596-621`.
Online (fetched 2026-07-03): BambuStudio master `CreatePresetsDialog.cpp` L434-499
(byte-identical generator); wiki.bambulab.com `create-filament` (custom filaments AMS-able
from firmware 1.6.6; only dialog-created presets sync) and `custom-filament-issue`
(cloud-only sync; unknown ids show `?`); blog.bambulab.com authorization-control (Jan-2025
AMS-config gating); bambulab/BambuStudio#5436 (firmware persists corrupted id);
OrcaSlicer #4431 (tray clobber to `?`), #3874 (cloud-only confirmation), PR #14459
(the `-f` invariant), PR #14423 + PR #12724 + PR #13315 (maintainer statements / community
P+md5 pressure); greghesp/ha-bambulab `filaments.json` (86/86 `GF*`) + #466/#540.
Method: 6 parallel evidence agents (generator semantics; AMS/device path; exhaustive
shape-dispatch sweep; upstream/online; tree-wide dry-run over 5892 presets / 1455 families;
branch change inventory) → 3-judge panel (device-ecosystem, identity-semantics,
migration-enforceability lenses). Dry-run artifacts: session scratchpad
`phex_dryrun_{summary,details,followup}.json`.
## 9. Open decisions for maintainers
1. **W4 scope**: none / 4 Qidi brand families (recommended) / + generic-tier re-adoption.
2. **W5 timing**: implement in-repo vs review upstream PR #13315 with the W1 hardening as a
prerequisite either way.
3. **W6**: pursue the filament-database-upload design (makes Orca ids device-visible and
ends the mimicry debate for good).

213
filament_id_plan_v3.md Normal file
View File

@@ -0,0 +1,213 @@
# filament_id plan v3: OFL as the catalog, content-addressed product ids
Supersedes §5 of `filament_id_plan_v2.md` (whose P+md5 verdict stands unchanged) and
revises the mint rule of `filament_id_plan.md` (v1, implemented on `feature/filament_id`).
Decision driver: v1's mint key scoped families to the *profile bundle* (printer brand), so
one commercial product tuned in N bundles carries N ids — e.g. PolyLite PLA ships in five
bundles (`BBL`, `OrcaFilamentLibrary`, `OrcaArena`, `Qidi`, `Snapmaker`), all with
`filament_vendor: "Polymaker"`, under up to five different ids (`GFL00`, `OFceJcLf`, …).
That contradicts what `filament_id` means: one id per commercial product line.
## 0. The architecture
Three tiers, two frozen islands:
1. **BBL island — untouched.** Bambu profiles keep their `GF*` catalog ids byte-for-byte
(device/RFID/cloud contract). No convergence between GF and OFL ids is attempted: the
same product may permanently carry `GFL00` on the BBL side and an `OF*` id everywhere
else. This is a deliberate trade for simplicity; v2's curated-GF-adoption (old W4) is
dropped.
2. **`QD_*` island — untouched.** Qidi's box builds `QD_<series>_<vendor>_<type>` from
device enums and requires exactly-matching presets (`QidiPrinterAgent.cpp:146-152`).
Qidi presets carrying `QD_*` are exempt from re-minting, forever.
3. **Everything else converges on OrcaFilamentLibrary.** OFL is the product catalog: a
material family's id is declared once, on its OFL family root. Vendor bundles carry
only per-printer *specializations* of OFL families — same base name, non-empty
`compatible_printers`, **no `filament_id` key** — which (a) alias-shadow the OFL preset
on the printers they claim (`Preset.cpp:3684-3714`: "Generic PLA @Qidi Q2 0.4mm nozzle"
hides "Generic PLA @System" on that machine) and (b) resolve the OFL family's id
through the loader's inherits/base-bundle walk (`PresetBundle.cpp:4842-5080`). Products
OFL does not (yet) carry mint their id in the vendor bundle **with the same rule**;
because the key is bundle-independent (§1), later hoisting the family into OFL never
changes its id.
Convergence therefore happens by *single declaration point*, not by copying ids across
bundles — no adoption registry, no cross-bundle id claims to curate.
## 1. The mint rule
```
triple = (filament_vendor, filament_type, family_name) # from the family ROOT's flattened config
key = "filament_product/<filament_vendor>/<filament_type>/<family_name>"
id = "OF" + base62_6( uuid5( FILAMENT_ID_NAMESPACE, key ) ) # namespace unchanged:
# c4d3ff49-4c32-5534-a3e3-00894157ab97
```
- **Triple resolution.** All three values come from the family root preset's *flattened
effective config* (`filament_vendor` and `filament_type` are inheritable list options —
take the first element; family_name = the root's base name, `\s?@.*` stripped once).
For own-key-layout families (no root), each declaring preset's flattened config is used
and CI requires all declarers of one family to agree on the triple. A root that resolves
an empty `filament_vendor` or `filament_type` is a CI error (generics use `"Generic"`).
Values enter the key verbatim (UTF-8, no case folding).
- **Bundle-independent by design.** The key contains no bundle name, so the same product
yields the same id whether minted in OFL or in a vendor bundle; migrating a family into
OFL (§0.3) is id-stable. Two bundles independently adding the same triple converge
automatically — and that is correct, because equal (vendor, type, name) *is* the
definition of "same product" here. The known look-alike hazards stay separated by the
type component: the 4 same-name-different-type groups (Flashforge `Generic PLA Silk` as
SILK, OFL `Generic PETG-CF` as PETG, `Generic PA6-CF`, `Generic PE-CF`) hash apart until
their type bugs are fixed (§5, W3) and converge automatically after — self-healing that
pure name-keying (rejected in v2) could not provide.
- **Format and salt unchanged from v1**: 8 chars, `OF` + 6 base62; deterministic `/1`,
`/2`… salt past any taken or retired id (the O+7 widening was evaluated and rejected:
62⁶ expects 1.9×10⁻⁵ collisions at today's 1,455 families, and `^O.{7}$` would sweep 35
legacy ids into the conformance gate vs. one today).
- **Content-addressed, deliberately.** If any triple component changes — a family rename,
a `filament_vendor` correction, a `filament_type` fix — the id changes with it. This
*replaces* v1's "ids are immutable once shipped" with "ids are derivable from the
product identity, and identity changes are migrations" — made safe by the succession
ledger (§2). `renamed_from` still gates preset-name compatibility as before.
- CLI: `--mint "Polymaker/PLA/PolyLite PLA"` prints without touching the tree; running
the script plain inserts missing ids; CI errors print the expected id.
## 2. The succession ledger (the amendment that makes this safe)
Shipped ids are referenced outside the tree: AFC/Klipper lane data, Bambu AMS trays
holding OFL-only materials, on-device PA-calibration records, user-root preset copies,
3mf `slice_info`. Retiring an id without a forwarding pointer downgrades all of those to
`Generic <type>` fallbacks. Therefore:
- **Schema.** `scripts/retired_filament_ids.json` entries become objects:
`{"retired": {"OGFL99": {"claims": [...], "successor": "OFxxxxxx"}}}`. Append-only as
before; a successor may itself be retired later (chains allowed, cycle-checked, and
followed to the live end). A retired id may never be minted again (check 4 unchanged).
Cross-island *hint* entries are permitted for ids Orca cannot retire because another
island owns them (e.g. `GFL99 → <OFL Generic PLA id>`): consulted only when no live
preset matches, so BBL installs still resolve `GFL99` natively first. By the same
principle, an id in a foreign island's space (`GF*`/`QD_*`) that vanishes from the tree
is **released with a hint, never retired** — the island's catalog owns it and may
legitimately (re)ship it later, which check 4 must never block. (Implementation
amendment, v3.0: `--update-snapshot` routes such ids to `hints` automatically; hint
keys may be absent from the tree.)
- **Shipped and consulted at runtime.** The ledger ships in `resources/`; a small helper
(`resolve_filament_id_succession(id)`) follows the chain and is consulted **only on
resolution miss**, before the `Generic <type>` name fallback, in:
`PresetBundle::get_filament_by_filament_id` (covers `DevFilaBlackList`, `SelectMachine`
warnings, `Plater` tray configs in one place), the AMS sync match predicates
(`PresetBundle.cpp:3151`, `:3252-3254` miss paths at `:3157-3169`/`:3260-3305`),
`PresetComboBoxes::add_ams_filaments`, `MachineObject::setting_id_to_type`
(`DeviceManager.cpp:2545` miss branch), calibration-history name lookup
(`CaliHistoryDialog.cpp:62`), and the #14423 Moonraker lane matching when it lands.
With this in place the OFL re-mint is near-residue-free and every future
content-addressed rename stays safe.
- **Kill the hardcoded generic map.** `MoonrakerPrinterAgent::map_filament_type_to_generic_id`
(`MoonrakerPrinterAgent.cpp:608-658`) hardcodes 23 OFL ids (`OGFL99``OFLSBS99`).
Replace it with a runtime lookup of the OFL generic preset by name ("Generic PLA
@System" → its current id), removing the code↔profile lockstep permanently. (This also
releases `OFLSBS99`, v2's one frozen OF-shaped legacy id, for normal re-minting.)
## 3. Validation rule changes
- **Check 3 (mint conformance)** becomes a pure function of the root's triple: a non-BBL,
non-`QD_*` id must equal `mint(triple)` ± salt, or be snapshot-grandfathered (the
grandfather set shrinks to ≈ nothing for non-BBL once migration completes).
- **Check 5 (alias hygiene) generalizes and becomes load-bearing.** For *every*
OFL-carried family (not just `Generic * @System`): a vendor specialization must keep the
OFL base name (else the OFL preset un-shadows and creates a live per-printer duplicate —
v1's P7 pattern) and non-empty `compatible_printers`, and must not declare an id. The
C++ validator's OFL-aware `-f` (`PresetBundle.cpp:5674-5753`) already enforces the
runtime consequence; the script check names the rename as the cause.
- **New check 8 (triple integrity):** every id-declaring family resolves a complete,
family-consistent triple; roots missing `filament_vendor`/`filament_type` error.
- **New check 9 (succession integrity):** every retired entry's successor chain ends at a
live tree id (or a documented cross-island hint target); no cycles; retired ids absent
from the tree.
- Snapshot mechanism, reserved spaces (`GF*`→BBL, `QD_*`→Qidi, `P`-hex/`null`→user-custom;
v2's do-not-reserve-`OF*` note stands), and check 1/2/4/7 are unchanged.
## 4. Migration phases
**v3.0 — tooling + prerequisites (no profile changes).** New mint + triple resolver
(reuse the flatten machinery), succession schema migration + C++ lookup helper wired into
the §2 miss paths, Moonraker map → runtime lookup, checks 3/5/8/9, tests. Carry v2's W1
client hardening (skip tray-wipe/temp-rewrite when a system preset holds the id; relax
the `DeviceManager.cpp:5252` assert; guard the `PresetBundle.cpp:3717` deref) — it ships
first regardless. **W3 type fixes land here**, *before* any re-mint: type is now a key
component, so minting before fixing OFL `Generic PETG-CF`/`Generic PE-CF` would re-id
those families twice.
**v3.1 — OFL re-mint.** Every OFL-declared id re-derives from its triple (mirrors like
`OGFA00`, generics like `OGFL99`, blocks like `OEPLAB00` — all of it); each old shipped id
gains a succession entry pointing at its replacement; the 26 GF-shaped ids OFL declares
(measured at v3.0: only one of them, `GFOT001`, is also declared by BBL) are released to
the BBL island space with hints at the re-minted families — two-island purity. Snapshot
regenerated; full gate battery + fixture overlays (this phase touches preset-visibility
machinery only via ids, but the fixtures are cheap insurance). Implementation notes
(v3.1, executed): OFL generic ids relocate from the fdm_filament_* template bases onto
the product-named "Generic X @System" presets first, so their triples carry the product
name; `fdm_filament_pc` keeps a (transitional) declaration because 7 Prusa presets
inherit it directly — the v3.2 Prusa worksheet re-homes them and deletes it.
**v3.2 — vendor bundles, worksheet-per-vendor (v1 machinery).** Three sub-cases:
(a) the **391 unshipped v1 `OF*` mints** re-derive under the triple key — no succession
entries (they never shipped; a documented one-time `--forget-never-shipped <list>` drops
them from the ledger lineage instead of retiring them, since the ledger's rationale —
ids live on in user presets and 3mfs — cannot apply to unreleased ids);
(b) **true generic tunings** riding copied legacy ids (`GFL99`-class) drop their own id
and re-point to the OFL generic family (the v1 Sovol pattern) — their old ids get
cross-island hints where BBL owns them, succession entries otherwise;
(c) remaining **shipped legacy ids** (numeric, name-shaped, pseudo-GF, the 57
multi-vendor GF residue, the 10 P-hex — everything non-BBL/non-`QD_*`, ~800 ids)
re-mint with succession entries. Retiring the 10 P-hex system ids also removes the last
system ids from `check_ams_filament_valid`'s destructive P-gate.
**v3.3 — ongoing consolidation (optional, per-vendor, id-stable).** Hoist vendor-unique
products into OFL where a family is genuinely multi-vendor material; thanks to the
bundle-independent key this never changes ids, so it can proceed opportunistically.
## 5. Gates (every phase)
`python scripts/orca_extra_profile_check.py` exit 0; `assign_filament_ids.py --check`
exit 0; unit tests green (existing 46 + new triple/succession/adoption tests); validator
`-l 2` exit 0, `-f` tree-wide exit 0, `-r` BBL+Qidi exit 0; custom-preset fixture
archives; config-equivalence — flattened effective configs differ only in `filament_id`
(re-mints), `inherits`/`compatible_printers` (re-points, as prescribed per worksheet), and
the W3 `filament_type` corrections. New for v3: a C++ test that a retired id resolves
through the succession chain in the sync miss path, and a Moonraker test that the generic
map lookup matches the shipped OFL presets. Manual AMS smoke test (tray set → old-id
resolve → clear) stays a release-checklist item.
## 6. Accepted costs (explicit)
- **GF ↔ OFL divergence is permanent** for products in both catalogs (PolyLite PLA ≠
`GFL00` outside BBL). The forward-looking fix is PR #12724-style filament-database
upload, where Orca ids become first-class device artifacts.
- **Renames/type-fixes re-id families** (by design); the succession ledger absorbs the
device/user residue, but each one is still a ledger entry and a snapshot diff to review.
- **User roots** keep whatever id they copied at creation; with succession lookup in
`get_filament_by_filament_id` they now *resolve* instead of dangling — strictly better
than v1's accepted residue.
- **One-time field transition**: devices holding pre-v3 ids (AFC lanes, AMS trays, cali
records) resolve via succession on updated clients; *older* Orca versions and
BambuStudio never resolved OFL-only ids anyway (`?`/generic fallback — unchanged for
them).
## 7. Open decisions
1. Whether v3.2(b)'s cross-island hints (`GFL99` → OFL generic) are wanted at launch or
deferred (pure-miss-path feature; zero risk to BBL installs, small review surface).
2. v3.3 pacing: per-vendor PRs opportunistically vs. a dedicated consolidation train.
3. Whether to fold v2's W5 (P+md5 ids for inherited *user* presets, PR #13315) into v3.0's
C++ work or keep it a separate PR (recommended: separate; W1 is its only prerequisite).
## 8. Evidence
Carried from v2 §8 (all re-verified this session), plus: `filament_vendor` audit — 49
distinct strings tree-wide, `"Polymaker"` byte-consistent across all five PolyLite-PLA
bundles; multi-vendor different-family GF residue = 57 ids; `MoonrakerPrinterAgent.cpp:
608-658` = 23 hardcoded OFL ids; mint examples verified live (`Qidi/PolyLite PLA`
`OFceJcLf` under the v1 key — the fragmentation this plan removes; salt determinism
`OF8afiMO`). Alias shadowing and the OFL id fallback verified at `Preset.cpp:3684-3714`
and `PresetBundle.cpp:4842-5080` during the v1 audit; per-printer `-f` semantics at
`PresetBundle.cpp:5674-5753`.

224
filament_id_plan_v4.md Normal file
View File

@@ -0,0 +1,224 @@
# filament_id plan v4: dissolve the Qidi `QD_*` island
Supersedes §0.2 of `filament_id_plan_v3.md` (the "`QD_*` island — untouched, forever"
decision) and amends every v3 section that carved out `QD_*`. Everything else in v3 —
the mint rule, the succession ledger, the BBL island, checks 19 — stands as
implemented (v3.0v3.2, all gates green at commit `f7c1b290fd`).
Decision driver: the island contradicts the catalog architecture v3 built. Qidi presets
carry 204 `QD_<series>_<vendor>_<typeidx>` ids over 264 declarations (49 families,
measured 2026-08-21) — one commercial product carries up to *five* ids (one per printer
series: `QIDI PLA Rapido` = `QD_0_1_1``QD_4_1_1`), which is exactly the
fragmentation v3 exists to remove. The island was frozen because Qidi's filament box
composes these ids from device enums and requires exactly-matching presets
(`QidiPrinterAgent.cpp:146-152`). But v3.0 shipped the machinery that makes freezing
unnecessary: the succession ledger already translates retired ids on resolution miss.
`QD_*` stops being a *preset id space* and becomes a *device protocol namespace*,
translated once at the agent edge.
## 0. The architecture change
Two tiers, **one** frozen island:
1. **BBL island — untouched, unchanged.** All v3 reasoning holds (device/RFID/cloud
contract is external and opaque).
2. **Everything else converges on OFL product ids — now including Qidi.** All 204
`QD_*` ids re-mint from their family triples and gain **retired** ledger entries
(not cross-island hints: after dissolution there is no island to own them, and
check 4's "never again" is exactly the guard we want against upstream re-adding
them). The box keeps working because `QidiPrinterAgent` resolves its composed
`QD_*` id through `resolve_filament_id_succession()` on miss — the same mechanism
every other retired id already uses.
Why retire rather than hint (the one real design choice here): hints exist for ids a
*foreign catalog* owns and may legitimately re-ship (`GF*`). Post-dissolution, nothing
may ever re-ship a `QD_*` id in the profile tree — the device composes them at
runtime, the tree translates them. Retired entries make CI enforce that permanently
(`--update-snapshot` refuses resurrections, check 4 refuses occurrences); hints would
permit re-shipping, which is now always a regression. The QD→family mapping is
strictly 1:1 (verified: no `QD_*` id is claimed by more than one family), so the
mode-rule successor is unambiguous for every entry.
## 1. What the dissolution consists of
Three independent work packages, ordered for bisectability:
- **(A) Re-converge the drifted tree** — prerequisite, not Qidi-specific. The
2026-07 main merge (`f3fa3a34bd`) brought upstream vendor updates in pre-v3 style:
`--check` currently exits with **243 errors** (123 unsanctioned new ids, 46 new
instantiated own-key presets, 31 override drifts, 18 reserved-space claims, 5
hint-keys re-declared — Qidi re-added `GFB99`/`GFG99`/`GFL99`, Snapmaker re-added
`GFG96`/`GFU99` and even four *retired* ids incl. `OGFL99`, plus Snapmaker U1
triple errors and a GreenGate3D rename). This is the standing WF-B maintenance
pass; the dissolution's gates cannot go green on a red base.
- **(B) C++ succession hook in the Qidi agent** — safe to land before any data
changes (pure miss-path: while `QD_*` presets still exist, the hook never fires).
- **(C) The dissolution proper** — tooling flip + Qidi data fixes + re-mint +
snapshot/ledger regeneration, one vendor worksheet in the v3.2 mold.
## 2. Runtime translation (work package B)
`QidiPrinterAgent.cpp:183-192` currently: compose `setting_id` → keep it if a visible
base preset declares it → else degrade to `filament_id_by_type(tray_type)` (i.e. every
QIDI-brand box slot silently becomes Generic once the ids re-mint). Insert the
succession walk between those two steps:
```cpp
} else if (!setting_id.empty() && has_visible_base_preset(bundle->filaments, setting_id)) {
tray.tray_info_idx = setting_id;
} else {
// Retired QD_* protocol ids forward to their minted successors via the shipped ledger.
const std::string successor = setting_id.empty() ? std::string()
: resolve_filament_id_succession(setting_id);
if (!successor.empty() && has_visible_base_preset(bundle->filaments, successor))
tray.tray_info_idx = successor;
else
tray.tray_info_idx = bundle->filaments.filament_id_by_type(tray.tray_type);
}
```
`resolve_filament_id_succession` is `Preset.hpp:119` (loads once, cycle-guarded,
empty-safe) — no new includes needed beyond what the file already reaches through
`PresetBundle`. This one hook covers both composition paths: the numeric-series
`build_setting_id` lambda *and* the non-numeric fallback
`map_filament_type_to_setting_id` (`:325-342`), whose four hardcoded returns
(`QD_1_0_1`/`_11`/`_41`/`_50` = Generic PLA/ABS/PETG/TPU 95A) become ledger keys in
package C. Keep that function as-is but extend its comment: the returned ids are
retired ledger keys by design, and `scripts/tests/test_filament_id.py` parses the
initializer (see §4 tests) — the Moonraker treatment (`MoonrakerPrinterAgent.cpp:
619-627`) of replacing the table with name lookups was considered and not taken: the
table already routes through the same ledger as the composed ids, and two translation
mechanisms in one agent is worse than one.
Tests (same commit):
- `tests/libslic3r/test_filament_id_succession.cpp`: add a section asserting a
`QD_`-shaped key forwards like any other (`{"QD_2_1_11", "OFnew001"}` resolves to
`"OFnew001"`) — pins that the walk is prefix-agnostic.
- `scripts/tests/test_filament_id.py`: new test parsing the four `QD_` literals out of
`QidiPrinterAgent.cpp::map_filament_type_to_setting_id` (mirror the parser in
`scripts/test_moonraker_lane_data.py`) and asserting each is a ledger key whose
chain terminates at a live tree id. **Add it marked expected-fail/skipped until
package C lands, then flip it on** — it is the permanent code↔ledger lockstep guard.
## 3. Tooling and validation flip (work package C, first commit)
All in `scripts/assign_filament_ids.py`; every touched line measured 2026-08-21:
- `is_island_declaration` (`:327-329`) → `return vendor == "BBL"`. This single change
pulls every `QD_*` declarer into the triple bookkeeping (`:385`) and thus into
checks 3 and 8, into `--remint`'s domain (`:1464`), and out of the hint-key
tolerances (`:877`, `:1100`).
- `reserved_space_owner` (`:596-604`): `QD_*` returns `(True, None)` — reserved,
ownerless, exactly like the P-hex/user-custom space. Consequences, all wanted:
check 6 refuses any future vendor claim; the `--update-snapshot` sanction gate
(`:938-963`) refuses new `QD_*` ids outright; the vanish path (`:994`) routes
`QD_*` to **retired** (owner `None` ≠ island), not released-with-hint. Update the
two message sites that render `owner is None` as "reserved for user-custom presets"
(`:628` docstring, `:958`, and check 6's copy) to name the space generically or
special-case `QD_*` ("Qidi device protocol; dissolved island — retired, never
declarable").
- Check 1 (`:667-668`): delete the `vendor == "Qidi" and fid.startswith("QD_")`
exemption.
- Module docstring (`:27-45`): rewrite the `QD_*` bullet — reserved space stays
listed, but as "device protocol namespace, translated via the succession ledger;
dissolved as a catalog island in v4, may never be declared".
- `scripts/tests/test_filament_id.py`: update the three island assertions —
`reserved_space_owner("QD_X4_PLA")``(True, None)` (`:403`),
`is_island_declaration("Qidi", "QD_X4_PLA")``False` (`:413`), and the
reserved-space message case (`:570`).
No changes to the ledger schema, the C++ checks, `--retire` (post-flip it accepts
`QD_*` olds automatically — they are non-island now), or the validator: check 3's
"non-BBL, non-`QD_*`" phrasing in v3 §3 was always implemented as "non-island", so the
flip *is* the spec change.
## 4. Migration phases
**v4.0 — re-converge the drifted tree (package A).** Per-vendor WF-B worksheets over
the 243 errors: Snapmaker U1 (triple divergence `Generic|Snapmaker|snapmaker`, empty
vendors, four resurrected retired ids — these force re-mints since retirement is
permanent), BBL/addnorth `GF_AN*` overrides (BBL island: grandfather via snapshot,
they are BBL-internal), Qidi/Snapmaker re-declared hint keys (re-mint those declarers,
the check's own prescription), GreenGate3D rename, then `--update-snapshot` (new
upstream `QD_*` ids sanction cleanly — the island is still intact in this phase) and
the full v3 §5 gate battery. Bump `version` in every touched
`resources/profiles/<Vendor>.json`. **Do not start v4.2 until `--check` exits 0.**
**v4.1 — the agent hook (package B).** §2 as written; `libslic3r_tests` +
`scripts/tests/test_filament_id.py` green; behavior-neutral by construction (no `QD_*`
ledger entries exist yet).
**v4.2 — the dissolution (package C).** One Qidi worksheet, v3.2 machinery:
1. Tooling flip commit (§3). `--check` now reports the Qidi island as
non-conformant — expected, red only between commits of this phase.
2. Data fixes, before any re-mint (the v3 "W3 lands first" lesson — type/vendor are
key components): add `filament_vendor: ["QIDI"]` at each QIDI-brand family's
inheritance apex so all declarers resolve it (39 of 49 families currently resolve
none — check 8a would refuse the mint). Verify the six Generic families
(`Generic PLA/ABS/PETG/PC/TPU 95A/PLA Silk`) resolve triples identical to their
OFL counterparts (vendor `Generic`, OFL's `filament_type`) so they *converge onto
the OFL ids* by triple math — the whole point; any mismatch is a W3-style data fix
here, not a fork. No `inherits`/`compatible_printers` re-pointing anywhere: the
series intermediates (`Generic PLA@Q2-Series` etc., all `instantiation: false`)
simply keep declarations whose values become the OFL ids.
3. `python scripts/assign_filament_ids.py --remint Qidi` — rewrites all 264
declarations in place to their family mints (same triple ⇒ same id across a
family's series intermediates and per-nozzle declarers; convergence with OFL ids
is legal by design, `want_id` already permits same-triple collisions `:1447`).
4. `--update-snapshot` — retires every vanished `QD_*` id with its mode-rule
successor. **Audit the ledger diff: all ~204 new entries must be `QD_*`→non-null.**
A null successor (possible only for a declared-only id with zero instantiated
claims) gets an explicit `--retire "QD_x=OFy"` with the family's minted id.
5. Un-skip the §2 lockstep test. Bump `resources/profiles/Qidi.json` version. Full
gate battery (§5).
Known residue, accepted: `QIDI PC-ABS-FR` (series 12) vs `QIDI PC/ABS-FR`
(series 34) are two preset-name families for one product → two ids. Renames were
ruled out in v3 (`renamed_from` rejected for migrations); if upstream ever unifies the
name, content-addressing re-ids and the ledger absorbs it — self-healing, no action
now.
**v4.3 — OFL consolidation (unchanged).** v3.3 stays optional and id-stable; QIDI-brand
families are vendor-unique and stay in the Qidi bundle.
## 5. Gates (per phase, delta from v3 §5)
Unchanged battery: `orca_extra_profile_check.py` exit 0, `assign_filament_ids.py
--check` exit 0, `scripts/tests/test_filament_id.py` all green, `libslic3r_tests`
green, validator `-l 2` tree-wide + `-f` tree-wide + `-v Qidi` exit 0, custom-preset
fixture archives (v4.2 touches no preset visibility, but they are cheap insurance —
run them for v4.0, which touches instantiation-adjacent upstream drift), flatten
config-equivalence. New for v4.2: equivalence diff may contain **only** `filament_id`
value changes (QD→OF) and the added `filament_vendor` keys on QIDI-brand families;
ledger diff audit per §4.4; the map-literal lockstep test. Manual release-checklist
item: Qidi box smoke test — slot holding a QIDI-brand material must surface the brand
preset (not Generic) on an updated client, via the memory-documented local Klipper
test rig.
## 6. Accepted costs (explicit, new relative to v3)
- **Every Qidi box slot resolves through the ledger miss-path forever** (one hash-map
walk per slot per status poll — negligible, and structurally identical to how
every retired id already resolves). The QD→OF translation table is maintained by
the retirement machinery, not by hand.
- **Older Orca clients** (pre-v4 profiles) paired with re-minted profile trees lose
QIDI-brand slot matching (they look up `QD_*` and fall back generic-by-type — the
degradation the hook removes for updated clients). Same one-time field-transition
shape v3 §6 already accepted for AMS/AFC ids.
- **Two ids for PC-ABS-FR** until upstream unifies the family name (§4 residue).
## 7. Evidence
Measured on this tree 2026-08-21 unless cited to v3: 204 distinct `QD_*` ids / 264
declaring presets / 49 families, QD→family strictly 1:1, per-series id sets
(`QD_0..4_1_1` = `QIDI PLA Rapido` etc.); 39 families resolve no `filament_vendor`;
declarations live on `instantiation:false` series intermediates (Q2/Q2C/X5/X4) and
per-nozzle X-Plus-4 presets; composition + miss-fallback at
`QidiPrinterAgent.cpp:146-152, 183-192`, hardcoded fallback table `:325-342`;
succession helpers `Preset.hpp:109-119`; ledger = 575 retired + 194 hints, zero `QD_*`
entries; snapshot holds 160 of the 204 (the 44 newcomers are post-merge drift);
`--check` = 243 errors, categorized in §1(A); island exemption mechanics at
`assign_filament_ids.py:327-329, 385, 596-604, 667, 877, 994, 1464, 1514`; `--remint`
same-triple convergence guard `:1447-1453`; retirement-permanence gate `:965-976`.

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"type": "filament",
"filament_id": "GFB00_01",
"filament_id": "OFLfywkp",
"setting_id": "wAJTMxtCY7EoavRi",
"name": "Afinia ABS+@HS",
"from": "system",

View File

@@ -1,6 +1,6 @@
{
"type": "filament",
"filament_id": "GFB00_01",
"filament_id": "OFV5wEMe",
"setting_id": "qCDnb2iBaz4hd4vX",
"name": "Afinia ABS@HS",
"from": "system",

View File

@@ -1,6 +1,6 @@
{
"type": "filament",
"filament_id": "GFA00_01",
"filament_id": "OF9HCdyQ",
"setting_id": "N3sCgjdjvp6FTtw9",
"name": "Afinia PLA@HS",
"from": "system",

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_tpu",
"from": "system",
"setting_id": "zUqTgAEbqTN1EdRl",
"filament_id": "GFU01_01",
"filament_id": "OFDJU6R3",
"instantiation": "true",
"filament_vendor": [
"Afinia"

View File

@@ -1,6 +1,6 @@
{
"type": "filament",
"filament_id": "GFB00_01",
"filament_id": "OFXi59OX",
"setting_id": "OxIiEYjbhEvSykaQ",
"name": "Afinia Value ABS@HS",
"from": "system",

View File

@@ -1,6 +1,6 @@
{
"type": "filament",
"filament_id": "GFA00_01",
"filament_id": "OFgHh8ly",
"setting_id": "BASsUdyvElEVJ9AA",
"name": "Afinia Value PLA@HS",
"from": "system",

View File

@@ -3,6 +3,6 @@
"name": "Anker Generic ABS @base",
"inherits": "fdm_filament_abs",
"from": "system",
"filament_id": "GFB99",
"filament_id": "OF3P0BQA",
"instantiation": "false"
}

View File

@@ -3,6 +3,6 @@
"name": "Anker Generic ASA @base",
"inherits": "fdm_filament_asa",
"from": "system",
"filament_id": "GFB98",
"filament_id": "OFRiyes1",
"instantiation": "false"
}

View File

@@ -3,6 +3,6 @@
"name": "Anker Generic PA @base",
"inherits": "fdm_filament_pa",
"from": "system",
"filament_id": "GFN99",
"filament_id": "OFMgJQCZ",
"instantiation": "false"
}

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic PA-CF @base",
"inherits": "fdm_filament_pa",
"from": "system",
"filament_id": "GFN98",
"filament_id": "OF7uE5KB",
"instantiation": "false",
"filament_type": [
"PA-CF"

View File

@@ -3,6 +3,6 @@
"name": "Anker Generic PC @base",
"inherits": "fdm_filament_pc",
"from": "system",
"filament_id": "GFC99",
"filament_id": "OFGPJxnt",
"instantiation": "false"
}

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic PETG @base",
"inherits": "fdm_filament_pet",
"from": "system",
"filament_id": "GFG99",
"filament_id": "OFgvOmM2",
"instantiation": "false",
"filament_type": [
"PETG"

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic PETG-CF @base",
"inherits": "fdm_filament_pet",
"from": "system",
"filament_id": "GFG98",
"filament_id": "OF1daEUW",
"instantiation": "false",
"filament_type": [
"PETG-CF"

View File

@@ -3,6 +3,6 @@
"name": "Anker Generic PLA @base",
"inherits": "fdm_filament_pla",
"from": "system",
"filament_id": "GFL99",
"filament_id": "OFndmfJ1",
"instantiation": "false"
}

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic PLA Silk @base",
"inherits": "fdm_filament_pla",
"from": "system",
"filament_id": "GFL96",
"filament_id": "OFXyyfX6",
"instantiation": "false",
"filament_cost": [
"20"

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic PLA+ @base",
"inherits": "fdm_filament_pla",
"from": "system",
"filament_id": "GFL95",
"filament_id": "OFIYTy8S",
"instantiation": "false",
"filament_cost": [
"25"

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic PLA-CF @base",
"inherits": "fdm_filament_pla",
"from": "system",
"filament_id": "GFL98",
"filament_id": "OFB0a3hT",
"instantiation": "false",
"filament_type": [
"PLA-CF"

View File

@@ -3,6 +3,6 @@
"name": "Anker Generic PVA @base",
"inherits": "fdm_filament_pva",
"from": "system",
"filament_id": "GFS99",
"filament_id": "OFsCbVbN",
"instantiation": "false"
}

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic TPU @base",
"inherits": "fdm_filament_tpu",
"from": "system",
"filament_id": "GFU99",
"filament_id": "OF6tKa1Z",
"instantiation": "false",
"filament_retraction_speed": "90",
"filament_deretraction_speed": "50",

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "RDI26qeSRSTe0PDg",
"filament_id": "GFB99",
"filament_id": "OF223rZv",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra 3 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "Moc1cylP6mmSQjlb",
"filament_id": "GFB99",
"filament_id": "OF223rZv",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra 3 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "2xgfnUKyOUQwYWPa",
"filament_id": "GFB99",
"filament_id": "OF223rZv",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra 3 Max 0.8 nozzle"

View File

@@ -4,11 +4,14 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "NOiAgxA0BbvLhR1v",
"filament_id": "GFB99",
"filament_id": "OF223rZv",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra S1 0.4 nozzle"
],
"filament_vendor": [
"Anycubic"
],
"filament_flow_ratio": [
"0.95"
],

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "MDWaiSjl89E6Edsn",
"filament_id": "GFABS",
"filament_id": "OF223rZv",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra S1 Max 0.25 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "wZjVYc7dBDSKRusJ",
"filament_id": "GFABS",
"filament_id": "OF223rZv",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "AbB6JtoPRsNgmxyy",
"filament_id": "GFABS",
"filament_id": "OF223rZv",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "exX565xnrLPkYkvu",
"filament_id": "GFABS",
"filament_id": "OF223rZv",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "KeEk3yQZrKmJY9Q0",
"filament_id": "GFABS",
"filament_id": "OF223rZv",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra X 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "Anycubic Generic ASA",
"from": "system",
"setting_id": "U66DGcpZeZ24VfPe",
"filament_id": "GFA99",
"filament_id": "OF0yFLkY",
"instantiation": "true",
"filament_vendor": [
"Anycubic"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "06j8OTuYw3Sh9BxL",
"filament_id": "GFA99",
"filament_id": "OF0yFLkY",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ASA @Anycubic Kobra 3 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "s2XJm0JghCSUDzhh",
"filament_id": "GFA99",
"filament_id": "OF0yFLkY",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ASA @Anycubic Kobra 3 Max 0.8 nozzle"

View File

@@ -4,11 +4,14 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "rf792xZNR7tRGKNg",
"filament_id": "GFA99",
"filament_id": "OF0yFLkY",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ASA @Anycubic Kobra S1 0.4 nozzle"
],
"filament_vendor": [
"Anycubic"
],
"filament_flow_ratio": [
"0.98"
],

View File

@@ -4,11 +4,14 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "wqK6AVf1mNkIOmnt",
"filament_id": "GFASA",
"filament_id": "OF0yFLkY",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ASA @Anycubic Kobra S1 Max 0.25 nozzle"
],
"filament_vendor": [
"Anycubic"
],
"filament_type": [
"ASA"
],

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "aMgpooFosBBvlKKR",
"filament_id": "GFASA",
"filament_id": "OF0yFLkY",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ASA @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "Isk7NkdH7GxsEZkS",
"filament_id": "GFASA",
"filament_id": "OF0yFLkY",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ASA @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "Ew0Lg6u3e5Xj19Bz",
"filament_id": "GFASA",
"filament_id": "OF0yFLkY",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ASA @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "PEg6aLXcyUc70p9F",
"filament_id": "GFASA",
"filament_id": "OF0yFLkY",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ASA @Anycubic Kobra X 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "az6wmXeWSCKcUEsK",
"filament_id": "GFB99",
"filament_id": "OF5Zk4e1",
"instantiation": "true",
"filament_flow_ratio": [
"0.926"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "qKcef8t7nj8nBjPu",
"filament_id": "GFB98",
"filament_id": "OFI80Hct",
"instantiation": "true",
"filament_flow_ratio": [
"0.93"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "j2PXsJyI9YyoE8lc",
"filament_id": "GFN98",
"filament_id": "OFPdhDlK",
"instantiation": "true",
"filament_type": [
"PA-CF"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "xzI6EgoKa3DFYyoZ",
"filament_id": "GFN99",
"filament_id": "OFB1Tv1E",
"instantiation": "true",
"nozzle_temperature_initial_layer": [
"280"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "3zgBtEkj1fQpDkxO",
"filament_id": "GFC99",
"filament_id": "OFWY6vBh",
"instantiation": "true",
"filament_max_volumetric_speed": [
"12"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "CEd987NP90bPdkW7",
"filament_id": "GFG99",
"filament_id": "OFWzr7m0",
"instantiation": "true",
"reduce_fan_stop_start_freq": [
"1"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pla",
"from": "system",
"setting_id": "9gFgyEJelaCIVwAH",
"filament_id": "GFL98",
"filament_id": "OFNcdCrm",
"instantiation": "true",
"filament_flow_ratio": [
"0.95"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pla",
"from": "system",
"setting_id": "2MiX54GMiZjFzwBI",
"filament_id": "GFL99",
"filament_id": "OFKyCgaK",
"instantiation": "true",
"filament_flow_ratio": [
"0.98"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pva",
"from": "system",
"setting_id": "I1GMbOsMYktU7EbM",
"filament_id": "GFS99",
"filament_id": "OFSGbf2I",
"instantiation": "true",
"filament_flow_ratio": [
"0.95"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_tpu",
"from": "system",
"setting_id": "xrbLc7y1AInpumtv",
"filament_id": "GFU99",
"filament_id": "OFGKLoUm",
"instantiation": "true",
"filament_max_volumetric_speed": [
"3.2"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "hcL5OHthGzApICTk",
"filament_id": "GFPA",
"filament_id": "OFzS7zt4",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PA @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "OJbFCKoG5DtVGT9J",
"filament_id": "GFPA",
"filament_id": "OFzS7zt4",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PA @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "6mMIVw8iheDn1OuV",
"filament_id": "GFPA",
"filament_id": "OFzS7zt4",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PA @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "epl6B2WNjV1dxG5Z",
"filament_id": "GFPA6-CF",
"filament_id": "OFQe7sAx",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PA6-CF @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "eMUXd3PFKHl41TIq",
"filament_id": "GFPA6-CF",
"filament_id": "OFQe7sAx",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PA6-CF @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "y9XYrUJrUYKcBhCI",
"filament_id": "GFPA6-CF",
"filament_id": "OFQe7sAx",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PA6-CF @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "nO6m8EaBKREWUi3f",
"filament_id": "GFPAHT-CF",
"filament_id": "OFaEuvNF",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PAHT-CF @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "ebKput7Vyn4IQez1",
"filament_id": "GFPAHT-CF",
"filament_id": "OFaEuvNF",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PAHT-CF @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pa",
"from": "system",
"setting_id": "jzrflGdwy9U8kl1c",
"filament_id": "GFPAHT-CF",
"filament_id": "OFaEuvNF",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PAHT-CF @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "7F0I28djSlDopbIp",
"filament_id": "GFPC",
"filament_id": "OFZtkHRq",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PC @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "wF10XxL2tNXh8C1p",
"filament_id": "GFPC",
"filament_id": "OFZtkHRq",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PC @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "t3VfcIKMk0ELiPP7",
"filament_id": "GFPC",
"filament_id": "OFZtkHRq",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PC @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "dOOXRvGwMe3NeUj0",
"filament_id": "GFPC-CF",
"filament_id": "OFeIyhUx",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PC-CF @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "H0IECyfsMaCMdefe",
"filament_id": "GFPC-CF",
"filament_id": "OFeIyhUx",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PC-CF @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "7nu7lbiAHMeFBIJc",
"filament_id": "GFPC-CF",
"filament_id": "OFeIyhUx",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PC-CF @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "puSuSfNyKlYlmLjL",
"filament_id": "GFPC-GF",
"filament_id": "OFxJRuVF",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PC-GF @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "1Qdmjm0MNJKU7aCD",
"filament_id": "GFPC-GF",
"filament_id": "OFxJRuVF",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PC-GF @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pc",
"from": "system",
"setting_id": "Z0qp7SdwN9luxLFO",
"filament_id": "GFPC-GF",
"filament_id": "OFxJRuVF",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PC-GF @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_tpu",
"from": "system",
"setting_id": "gmni9H2Du5nxBqQu",
"filament_id": "GFU99",
"filament_id": "OFUaHEO9",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PEBA 95A @Anycubic Kobra 3 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_tpu",
"from": "system",
"setting_id": "zCqTTjac3ydSAw3q",
"filament_id": "GFPEBA",
"filament_id": "OFjLAYgO",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PEBA @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_tpu",
"from": "system",
"setting_id": "1bCJcP2RcDlvIqEo",
"filament_id": "GFPEBA",
"filament_id": "OFjLAYgO",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PEBA @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_tpu",
"from": "system",
"setting_id": "5v6coSKjAl220Cnq",
"filament_id": "GFPEBA",
"filament_id": "OFjLAYgO",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PEBA @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "saRQ24o8KHgc1YnM",
"filament_id": "GFPET-CF",
"filament_id": "OFRhCTUg",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PET-CF @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "hYFR2CtiXab9KZUp",
"filament_id": "GFPET-CF",
"filament_id": "OFRhCTUg",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PET-CF @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "RCaf3c84JsWkuJzd",
"filament_id": "GFPET-CF",
"filament_id": "OFRhCTUg",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PET-CF @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "Anycubic Generic PETG",
"from": "system",
"setting_id": "qe2wYLf7CEMjmfxj",
"filament_id": "GFG99",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_vendor": [
"Anycubic"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "uNktFymJ3RbGa7k0",
"filament_id": "GFG99",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra 3 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "EaW663aw4torYXG8",
"filament_id": "GFG99",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra 3 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "lgVEMmBHMen0sWsI",
"filament_id": "GFG99",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra 3 Max 0.8 nozzle"

View File

@@ -4,11 +4,14 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "uLRMTzyJIara4Hr2",
"filament_id": "GFG99",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra S1 0.4 nozzle"
],
"filament_vendor": [
"Anycubic"
],
"filament_flow_ratio": [
"0.96"
],

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "FAyFkKpV5w6G5dU2",
"filament_id": "GFPETG",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra S1 Max 0.25 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "duduwoswLHk9I9sY",
"filament_id": "GFPETG",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "b3GU7ytZPEYNNyne",
"filament_id": "GFPETG",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "SrHKJh6prBzMz4Se",
"filament_id": "GFPETG",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "HyTi93UGno6uAJEO",
"filament_id": "GFPETG",
"filament_id": "OFo6n2pB",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra X 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "ajFzrQq9AvGtZCui",
"filament_id": "GFPETG-CF",
"filament_id": "OF9UJOGF",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG-CF @Anycubic Kobra S1 Max 0.4 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "VOV59iBwqwMtmi48",
"filament_id": "GFPETG-CF",
"filament_id": "OF9UJOGF",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG-CF @Anycubic Kobra S1 Max 0.6 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "L6GIF36jbXf5NVOw",
"filament_id": "GFPETG-CF",
"filament_id": "OF9UJOGF",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG-CF @Anycubic Kobra S1 Max 0.8 nozzle"

View File

@@ -4,7 +4,7 @@
"inherits": "Anycubic Generic PLA",
"from": "system",
"setting_id": "XIq1GeZdHqOl9Gtr",
"filament_id": "GFL92",
"filament_id": "OFmjN2bc",
"instantiation": "true",
"filament_vendor": [
"Anycubic"

View File

@@ -4,7 +4,7 @@
"inherits": "Anycubic Generic PLA",
"from": "system",
"setting_id": "EaVdXQHAyocYUCez",
"filament_id": "GFL99",
"filament_id": "OFmjN2bc",
"instantiation": "true",
"filament_vendor": [
"Anycubic"

View File

@@ -4,7 +4,7 @@
"inherits": "Anycubic Generic PLA",
"from": "system",
"setting_id": "UpC1Qut0kjYRD9po",
"filament_id": "GFL92",
"filament_id": "OFmjN2bc",
"instantiation": "true",
"filament_vendor": [
"Anycubic"

Some files were not shown because too many files have changed in this diff Show More