This commit is contained in:
SoftFever
2026-07-02 21:57:27 +08:00
parent dbe99d0d6f
commit 051cdd4560
3 changed files with 1439 additions and 0 deletions

1029
filament_id_plan.html Normal file

File diff suppressed because it is too large Load Diff

397
filament_id_plan.md Normal file
View File

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

13
task.md Normal file
View File

@@ -0,0 +1,13 @@
check commit c2e91cb86ce013dc0486419f21620975c220c3be and it's related PR.
currently most of the system profiles didn't follow the Bambu's profiles rule of filemant id.
we want to come up a rule for filament id generation for all vendors and a fixing plan for existing system profiles so that there is no ambiguous filament ids for any given printer.
We have two major goals:
### goal 1
figure out a strategy or filament id generation rule. we want to follow bambu's rule but they only care about their own profiles while we need to consider generic. and the filament id generation rule should be easy to understand and maintain for not just us but other profiles creators too. either a determistic rule by using script like setting_id rule. or a documented rule so developers can refer when manually creating profiles. and figure out a way to fix migrate/fix existing filament ids in current profiles. note: bambu profiles shouldn't be touch to maintain interoperbitly with their AMS.
### goal 2
in existing system profiles, the ambiguous can caused by different error patterns:
1. legit sub type don't have unique filament id due to copy-paste when creating profiles. in this case, we should assign filament unique filament id to it.
2. generic and specific filament with legit same filament id adds same printer into it's compatible_printers. in this case we should remove the printer from the generic filament's compatible_printers.
3. we need to figure out more error patterns and figure out the fix strategy