Compare commits

..

11 Commits

Author SHA1 Message Date
Kris Austin
d61e0cb7bf build: unify the warning policy across compilers (clang-cl: 124k warnings -> 2.7k, 21% faster) (#15328) 2026-08-23 19:55:48 -03:00
TheLegendTubaGuy
07b81cfdc9 Fix Qidi X-Max 4 chamber heating profiles (#15244) 2026-08-23 13:10:41 -03:00
Valentin
d7fed95390 Fix Ukrainian translation typo (#15333) 2026-08-23 12:42:30 -03:00
GlauTech
c72908e377 Update OrcaSlicer_tr.po (#15309)
* Update OrcaSlicer_tr.po

* Update OrcaSlicer_tr.po

Fixed inaccurate AI-generated text and updated missing translations.

* REmoive # AI Translated

* Update OrcaSlicer_tr.po

The following changes were made in this version:
- The term "Instance" was changed to "Eş kopya".
- The term "Jerk" was changed to "sarsıntı".
- Semantic discrepancies regarding certain words were corrected.

* Update OrcaSlicer_tr.po

The necessary arrangements have been made.

* Update OrcaSlicer_tr.po

* Update OrcaSlicer_tr.po

The necessary updates have been made.

* G-kodu to G-code

---------

Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
2026-08-23 12:34:51 -03:00
Ian Bassi
ea376e858c Show move length in Previewr panel (#15326)
Adds a new "Length" row to the sequential marker position popup in GCodeViewer and updates row capacity accordingly.
For arc commands (G2/G3) that are split into multiple vertices, it now sums segment distances across vertices with the same gcode_id so the displayed value reflects the full move length instead of a single chord.
2026-08-23 12:18:22 -03:00
Ian Bassi
877180829c Unify colinear simplify tolerance in Arachne (#15314)
Introduced a shared `colinear_vertex_tolerance()` helper in `ExtrusionLine.hpp` and updated both simplify paths (`ExtrusionLine.cpp` and `WallToolPaths.cpp`) to use it instead of duplicated hardcoded `0.005` scaled thresholds. This keeps the near-colinear early-out tied to `SCALED_EPSILON` (rounding-noise scale) and avoids unintended curve decimation from larger tolerances, while documenting the geometric impact in code.
2026-08-23 12:18:06 -03:00
Anthony Cox
550e234a37 Newer GCCs are bitching about in-class initialisation. Lets fix that! (#15292) 2026-08-22 19:23:59 -03:00
Noisyfox
3b4e65d8a9 Fix thin wall fuzzy (#14309)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-08-22 18:46:01 -03:00
Kris Austin
4b397fc2cc fix: slice the same model to the same lightning infill every time (#15311) 2026-08-21 22:24:39 -03:00
Kris Austin
90f76fa28c fix: restore compile parallelism for clang-cl builds with the Visual Studio generator (#15324) 2026-08-21 15:59:16 -03:00
Valerii Bokhan
f05444dc94 Fix unstable contours from triangulated planar faces (redone) (#15316) 2026-08-21 10:50:58 -03:00
3605 changed files with 3443 additions and 31545 deletions

View File

@@ -20,8 +20,6 @@
> 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,14 +64,13 @@ 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]}
# 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
# For now run filament subtype check only for BBL profiles until we fix other vendors' profiles.
- name: validate filament subtype check for BBL profiles
id: validate_filament_subtypes
continue-on-error: true
run: |
set +e
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 -f 2>&1 | tee ${{ runner.temp }}/validate_filament_subtypes.log
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 -v BBL -f 2>&1 | tee ${{ runner.temp }}/validate_filament_subtypes.log
exit ${PIPESTATUS[0]}
- name: validate custom presets
@@ -218,7 +217,7 @@ jobs:
fi
if [ "${{ steps.validate_filament_subtypes.outcome }}" = "failure" ]; then
echo "### Filament Subtype Validation Failed"
echo "### BBL Filament Subtype Validation Failed"
echo ""
echo '```'
head -c 30000 ${{ runner.temp }}/validate_filament_subtypes.log || echo "No output captured"

View File

@@ -59,6 +59,13 @@ if (APPLE)
message(STATUS "CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET}")
endif ()
# Keep MSVC's default /W3 out of CMAKE_<LANG>_FLAGS so it can be applied to our own
# targets only. Silencing a bundled target would otherwise override a warning level,
# which cl reports as D9025 for every file it compiles.
if (POLICY CMP0092)
cmake_policy(SET CMP0092 NEW)
endif ()
project(OrcaSlicer)
# Backward compatibility for old CMake versions
@@ -126,6 +133,8 @@ option(SLIC3R_GUI "Compile OrcaSlicer with GUI components (OpenGL,
option(SLIC3R_FHS "Assume OrcaSlicer is to be installed in a FHS directory structure" 0)
option(SLIC3R_PROFILE "Compile OrcaSlicer with an invasive Shiny profiler" 0)
option(SLIC3R_PCH "Use precompiled headers" 1)
option(SLIC3R_WARNINGS "Emit compiler warnings for OrcaSlicer sources" 1)
option(SLIC3R_BUNDLED_WARNINGS "Emit compiler warnings for bundled third-party sources" 0)
option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
@@ -335,15 +344,20 @@ if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
# clang-cl can interpret SYSTEM header paths if -imsvc is used
set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-imsvc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \
-Wno-old-style-cast -Wno-reserved-id-macro -Wno-c++98-compat-pedantic")
else ()
set(IS_CLANG_CL FALSE)
endif ()
if (MSVC)
if (SLIC3R_MSVC_COMPILE_PARALLEL AND NOT IS_CLANG_CL)
# CMP0092 only applies when the cache is created; an existing tree keeps its /W3,
# which a silenced bundled target would then override (D9025, once per file).
string(REGEX REPLACE "/W[0-4]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
# /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")
add_compile_options(/MP)
endif ()
# /bigobj (Increase Number of Sections in .Obj file)
@@ -523,8 +537,15 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals" )
endif()
if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
if (NOT MINGW)
if ((NOT MSVC OR IS_CLANG_CL) AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
if (IS_CLANG_CL)
# clang-cl reads -Wall as MSVC /Wall, which clang maps to -Weverything. /W4 is
# its -Wall -Wextra and, unlike /clang:-Wall, is ordered with the -Wno-* below
# instead of after them. The -Wextra-only warnings are dropped again so the set
# matches what -Wall gives the GNU/Clang builds.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4" )
add_compile_options(-Wno-unused-parameter -Wno-ignored-qualifiers -Wno-missing-field-initializers)
elseif (NOT MINGW)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall" )
endif ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder" )
@@ -1086,8 +1107,57 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
endfunction()
# Bundled sources set their own warning flags, and a plain -Wall there means /Wall
# (= -Weverything) under clang-cl. Target options are applied after the ones a target
# set on itself, so these win. Targets are discovered rather than listed so a newly
# bundled library needs no maintenance here.
function(orcaslicer_silence_third_party_warnings _dir)
get_property(_subdirs DIRECTORY "${_dir}" PROPERTY SUBDIRECTORIES)
foreach (_subdir IN LISTS _subdirs)
orcaslicer_silence_third_party_warnings("${_subdir}")
endforeach ()
get_property(_targets DIRECTORY "${_dir}" PROPERTY BUILDSYSTEM_TARGETS)
foreach (_target IN LISTS _targets)
get_target_property(_type ${_target} TYPE)
if (NOT _type STREQUAL "INTERFACE_LIBRARY" AND NOT _type STREQUAL "UTILITY")
if (MSVC AND NOT IS_CLANG_CL)
# Drop any level the target set for itself, or -w overrides it and cl
# reports D9025 once per file.
get_target_property(_opts ${_target} COMPILE_OPTIONS)
if (_opts)
string(REGEX REPLACE "/W[0-4]|/Wall" "" _opts "${_opts}")
string(REGEX REPLACE ";;+" ";" _opts "${_opts}")
set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${_opts}")
endif ()
# CMake maps a level into the VS generator's WarningLevel element, while a
# bare -w stays on the command line and trips D9025 there, once per file.
target_compile_options(${_target} PRIVATE /W0)
else ()
target_compile_options(${_target} PRIVATE -w)
endif ()
endif ()
endforeach ()
endfunction()
# libslic3r, OrcaSlicer GUI and the OrcaSlicer executable.
add_subdirectory(deps_src)
if (NOT SLIC3R_BUNDLED_WARNINGS)
orcaslicer_silence_third_party_warnings("${CMAKE_CURRENT_SOURCE_DIR}/deps_src")
endif ()
# Warning level for the targets added below: our sources, plus glad and libvgcode,
# which are vendored but live under src/. The deps_src libraries were configured just
# above. CMP0092 left MSVC without a default level, so it is set here.
if (NOT SLIC3R_WARNINGS)
add_compile_options(-w)
elseif (MSVC AND NOT IS_CLANG_CL)
# /we4715 is C4715, no return from a non-void function, matching the
# -Werror=return-type the GNU/Clang builds apply.
add_compile_options(/W3 /we4715)
endif ()
add_subdirectory(src)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT OrcaSlicer_app_gui)
@@ -1099,6 +1169,10 @@ endif()
if(BUILD_TESTS)
add_subdirectory(tests)
if (NOT SLIC3R_BUNDLED_WARNINGS)
# Catch2 is vendored under tests/ and sets its own warning flags too.
orcaslicer_silence_third_party_warnings("${CMAKE_CURRENT_SOURCE_DIR}/tests/catch2")
endif ()
endif()
if (NOT WIN32 AND NOT APPLE)

View File

@@ -1,211 +0,0 @@
# 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).

File diff suppressed because it is too large Load Diff

View File

@@ -1,397 +0,0 @@
# 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.

View File

@@ -1,314 +0,0 @@
# 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).

View File

@@ -1,213 +0,0 @@
# 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`.

View File

@@ -1,224 +0,0 @@
# 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

@@ -4142,10 +4142,10 @@ msgid "PA Profile"
msgstr "Профіль PA"
msgid "Factor K"
msgstr "Коэф. K"
msgstr "Коеф. K"
msgid "Factor N"
msgstr "Коэф. N"
msgstr "Коеф. N"
msgid "Setting AMS slot information while printing is not supported"
msgstr "Зміна інформації про слоти AMS під час друку не підтримується"

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{
"type": "filament",
"filament_id": "OF9HCdyQ",
"filament_id": "GFA00_01",
"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": "OFDJU6R3",
"filament_id": "GFU01_01",
"instantiation": "true",
"filament_vendor": [
"Afinia"

View File

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

View File

@@ -1,6 +1,6 @@
{
"type": "filament",
"filament_id": "OFgHh8ly",
"filament_id": "GFA00_01",
"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": "OF3P0BQA",
"filament_id": "GFB99",
"instantiation": "false"
}

View File

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

View File

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

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic PA-CF @base",
"inherits": "fdm_filament_pa",
"from": "system",
"filament_id": "OF7uE5KB",
"filament_id": "GFN98",
"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": "OFGPJxnt",
"filament_id": "GFC99",
"instantiation": "false"
}

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic PETG @base",
"inherits": "fdm_filament_pet",
"from": "system",
"filament_id": "OFgvOmM2",
"filament_id": "GFG99",
"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": "OF1daEUW",
"filament_id": "GFG98",
"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": "OFndmfJ1",
"filament_id": "GFL99",
"instantiation": "false"
}

View File

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

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic PLA+ @base",
"inherits": "fdm_filament_pla",
"from": "system",
"filament_id": "OFIYTy8S",
"filament_id": "GFL95",
"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": "OFB0a3hT",
"filament_id": "GFL98",
"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": "OFsCbVbN",
"filament_id": "GFS99",
"instantiation": "false"
}

View File

@@ -3,7 +3,7 @@
"name": "Anker Generic TPU @base",
"inherits": "fdm_filament_tpu",
"from": "system",
"filament_id": "OF6tKa1Z",
"filament_id": "GFU99",
"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": "OF223rZv",
"filament_id": "GFB99",
"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": "OF223rZv",
"filament_id": "GFB99",
"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": "OF223rZv",
"filament_id": "GFB99",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ABS @Anycubic Kobra 3 Max 0.8 nozzle"

View File

@@ -4,14 +4,11 @@
"inherits": "fdm_filament_abs",
"from": "system",
"setting_id": "NOiAgxA0BbvLhR1v",
"filament_id": "OF223rZv",
"filament_id": "GFB99",
"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": "OF223rZv",
"filament_id": "GFABS",
"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": "OF223rZv",
"filament_id": "GFABS",
"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": "OF223rZv",
"filament_id": "GFABS",
"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": "OF223rZv",
"filament_id": "GFABS",
"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": "OF223rZv",
"filament_id": "GFABS",
"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": "OF0yFLkY",
"filament_id": "GFA99",
"instantiation": "true",
"filament_vendor": [
"Anycubic"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "06j8OTuYw3Sh9BxL",
"filament_id": "OF0yFLkY",
"filament_id": "GFA99",
"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": "OF0yFLkY",
"filament_id": "GFA99",
"instantiation": "true",
"filament_settings_id": [
"Anycubic ASA @Anycubic Kobra 3 Max 0.8 nozzle"

View File

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

View File

@@ -4,14 +4,11 @@
"inherits": "fdm_filament_asa",
"from": "system",
"setting_id": "wqK6AVf1mNkIOmnt",
"filament_id": "OF0yFLkY",
"filament_id": "GFASA",
"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": "OF0yFLkY",
"filament_id": "GFASA",
"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": "OF0yFLkY",
"filament_id": "GFASA",
"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": "OF0yFLkY",
"filament_id": "GFASA",
"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": "OF0yFLkY",
"filament_id": "GFASA",
"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": "OF5Zk4e1",
"filament_id": "GFB99",
"instantiation": "true",
"filament_flow_ratio": [
"0.926"

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "CEd987NP90bPdkW7",
"filament_id": "OFWzr7m0",
"filament_id": "GFG99",
"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": "OFNcdCrm",
"filament_id": "GFL98",
"instantiation": "true",
"filament_flow_ratio": [
"0.95"

View File

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

View File

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

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_tpu",
"from": "system",
"setting_id": "xrbLc7y1AInpumtv",
"filament_id": "OFGKLoUm",
"filament_id": "GFU99",
"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": "OFzS7zt4",
"filament_id": "GFPA",
"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": "OFzS7zt4",
"filament_id": "GFPA",
"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": "OFzS7zt4",
"filament_id": "GFPA",
"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": "OFQe7sAx",
"filament_id": "GFPA6-CF",
"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": "OFQe7sAx",
"filament_id": "GFPA6-CF",
"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": "OFQe7sAx",
"filament_id": "GFPA6-CF",
"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": "OFaEuvNF",
"filament_id": "GFPAHT-CF",
"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": "OFaEuvNF",
"filament_id": "GFPAHT-CF",
"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": "OFaEuvNF",
"filament_id": "GFPAHT-CF",
"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": "OFZtkHRq",
"filament_id": "GFPC",
"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": "OFZtkHRq",
"filament_id": "GFPC",
"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": "OFZtkHRq",
"filament_id": "GFPC",
"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": "OFeIyhUx",
"filament_id": "GFPC-CF",
"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": "OFeIyhUx",
"filament_id": "GFPC-CF",
"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": "OFeIyhUx",
"filament_id": "GFPC-CF",
"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": "OFxJRuVF",
"filament_id": "GFPC-GF",
"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": "OFxJRuVF",
"filament_id": "GFPC-GF",
"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": "OFxJRuVF",
"filament_id": "GFPC-GF",
"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": "OFUaHEO9",
"filament_id": "GFU99",
"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": "OFjLAYgO",
"filament_id": "GFPEBA",
"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": "OFjLAYgO",
"filament_id": "GFPEBA",
"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": "OFjLAYgO",
"filament_id": "GFPEBA",
"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": "OFRhCTUg",
"filament_id": "GFPET-CF",
"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": "OFRhCTUg",
"filament_id": "GFPET-CF",
"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": "OFRhCTUg",
"filament_id": "GFPET-CF",
"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": "OFo6n2pB",
"filament_id": "GFG99",
"instantiation": "true",
"filament_vendor": [
"Anycubic"

View File

@@ -4,7 +4,7 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "uNktFymJ3RbGa7k0",
"filament_id": "OFo6n2pB",
"filament_id": "GFG99",
"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": "OFo6n2pB",
"filament_id": "GFG99",
"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": "OFo6n2pB",
"filament_id": "GFG99",
"instantiation": "true",
"filament_settings_id": [
"Anycubic PETG @Anycubic Kobra 3 Max 0.8 nozzle"

View File

@@ -4,14 +4,11 @@
"inherits": "fdm_filament_pet",
"from": "system",
"setting_id": "uLRMTzyJIara4Hr2",
"filament_id": "OFo6n2pB",
"filament_id": "GFG99",
"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": "OFo6n2pB",
"filament_id": "GFPETG",
"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": "OFo6n2pB",
"filament_id": "GFPETG",
"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": "OFo6n2pB",
"filament_id": "GFPETG",
"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": "OFo6n2pB",
"filament_id": "GFPETG",
"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": "OFo6n2pB",
"filament_id": "GFPETG",
"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": "OF9UJOGF",
"filament_id": "GFPETG-CF",
"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": "OF9UJOGF",
"filament_id": "GFPETG-CF",
"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": "OF9UJOGF",
"filament_id": "GFPETG-CF",
"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": "OFmjN2bc",
"filament_id": "GFL92",
"instantiation": "true",
"filament_vendor": [
"Anycubic"

View File

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

View File

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

View File

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

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