From 051cdd45600398399cc5d1f300112f24bc37ced7 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Thu, 2 Jul 2026 21:57:27 +0800 Subject: [PATCH] init --- filament_id_plan.html | 1029 +++++++++++++++++++++++++++++++++++++++++ filament_id_plan.md | 397 ++++++++++++++++ task.md | 13 + 3 files changed, 1439 insertions(+) create mode 100644 filament_id_plan.html create mode 100644 filament_id_plan.md create mode 100644 task.md diff --git a/filament_id_plan.html b/filament_id_plan.html new file mode 100644 index 0000000000..08f4a06b74 --- /dev/null +++ b/filament_id_plan.html @@ -0,0 +1,1029 @@ + + + + + +filament_id — design & migration plan · OrcaSlicer + + + + + + +
+ + +
+
DESIGN REVIEW resources/profiles · filament_id
+

One id, one material — on every printer, exactly once.

+

A filament_id names a material family: one commercial product line, shared by all of its + per-printer variants. Devices match spools by filament_id + printer compatibility. + That only works if, for any one printer, at most one compatible preset carries a given id. Bambu keeps this + invariant; the other 64 vendor bundles broke it 1,256 times.

+ +
+ id anatomy — Bambu's grammar + + + + GF + A + 00 + + + + + + + + + + + + prefix — Bambu's namespace + frozen: AMS RFID tags carry these bytes + family letter — A: Bambu PLA + B ABS/ASA · C PC · G PETG · L generic/3rd-party PLA + N PA · P PP/PE · S support · T PET/PPS · U TPU + tier number + 00–49 branded · 50–59 fiber-filled + 60–70 partners · 95–99 generic, desc. + + + + shared by every variant: + + + Bambu PLA Basic @BBL X1C + + Bambu PLA Basic @BBL P1P + + Bambu PLA Basic @BBL X1C 0.2 nozzle + + + +
+ +
+
1,256
printer-level ambiguity errors (validator -f, tree-wide)
+
356
logical collision groups after de-duplication
+
30 / 65
vendor bundles affected / total
+
0
violations in BBL — cleaned in PR #14459, CI-gated
+
+

Every number in this document is reproducible: an independent loader-faithful audit re-derives the + validator's output exactly (1256 = 1256), and every code claim carries a file:line reference.

+
+ + +
+
§0 overview
+

The plan in one screen

+

Two deliverables: a generation rule so ids can never become ambiguous again (goal 1), and a + migration that fixes the 356 existing collisions without breaking users (goal 2).

+ +
+ the whole plan + + + + + + + + + TODAY + 7+ ad-hoc id schemes + 356 collision groups + zero written rules + CI covers BBL only + + + RULE + TOOLING §3 §6 + deterministic OF* mint + id lives on @base only + CI ratchets + snapshot + no profile changes yet + + + MIGRATION §5 + fresh ids only, ever + OFL first, then per vendor + names never change + one reviewable PR each + + + DONE + invariant holds + tree-wide, + by construction + + + + + + + BBL profiles are never touched · Qidi QD_* device ids are never touched · no preset is renamed or deleted + +
+ +
+ Why migration is safe — verified in code: user presets re-derive filament_id from their parent on + every load Preset.cpp:1658-1682; 3mf projects resolve presets by name + config, not id + Preset.cpp:2490-2576; Klipper, Creality and Snapmaker derive tray ids at runtime from the + installed bundle. Fresh, never-reused ids on non-BBL system presets break nothing. +
+
+ + +
+
§1 how matching works
+

Every ecosystem funnels through this one id

+

Not just Bambu. Five device ecosystems put a filament_id into the same pipeline; the slicer then + picks the single preset that matches the id and is compatible with the active printer.

+ +
+ runtime — device → preset + + + + + + + + + + BBL AMS + from device RFID — frozen + + Qidi box + QD_<series>_<v>_<t> — frozen + + Creality CFS + scored at runtime — safe + + Klipper AFC/HH + by filament type — safe + + Snapmaker + color/type match — safe + + + + + + + + + + + + tray_info_idx (MQTT / agent) + + filament_ams_list["filament_id"] + + find_if( f.is_compatible && base(f)==f && f.filament_id == id ) + + + + + + + + Plater.cpp:3440 + PresetBundle.cpp:3132/3233 + + + → the ONE matching preset + two matches? find_if silently returns whichever sorts first — no log, no warning; the AMS edit dialog even hides the second preset + +
+ +

The invariant, drawn

+
+ per printer × per id +
+
+

✓ Correct — Bambu PLA Basic, id GFA00

+ + + + + +
printer presetcompatible presets with GFA00
X1C…@BBL X1C 1
X1C 0.2 nozzle…@BBL X1C 0.2 nozzle 1
P1P…@BBL P1P 1
+

Variants share the id; their compatible_printers are disjoint. Each printer sees the id exactly once.

+
+
+

✗ Broken — Qidi, id GFB99 (real case)

+ + + +
printer presetcompatible presets with GFB99
X-Max 3 0.4 nozzleBambu ABS · HATCHBOX ABS · Overture ABS · PolyLite ABS · QIDI ABS Rapido · QIDI ABS-GF · QIDI ASA · ASA-Aero · PC-ABS-FR · Generic ABS … 16
+

Sixteen different materials answer to one id on one printer. A spool tagged “generic ABS” matches + whichever preset sorts first — and GFB99 was stamped into 317 Qidi files.

+
+
+
+ +

Two consumers you can't see from the profiles

+

Most matching is printer-scoped, but several code paths match globally, by id alone — tray display name, + temperature_vitrification warnings, calibration history, and the multi-nozzle grouping key + FilamentGroup.cpp:513. So two different materials must never share an id even across + vendors: the first preset in the whole installed collection supplies the name, type and temperature data.

+
Two identity systems. The library (OFL) hides its global presets per printer by + alias (name before  @) — Preset.cpp:3684 — while devices match by + filament_id. When a vendor tunes Generic PLA but renames it + Flashforge PLA Basic, the alias no longer matches, shadowing fails, and both presets are + visible with the same id. The rule in §3 aligns the two systems: one family = one alias = one id.
+
+ + +
+
§2 the landscape today
+

Seven schemes, zero rules, one epidemic

+

The only guidance that ever existed was “≤ 8 characters”, checked for BBL only. Every other + convention was one contributor's invention, merged without comment, then imitated.

+ +
+ who uses which id shape — 5,866 instantiated presets + + + GF<letter><NN> (Bambu classic, mass-copied) + 2,921 + free-form ("GFPLA Silk", "LHF_pla", 36-char names…) + 2,241 + GF<BRAND><NNN> (Bambu partner ids) + 395 + O-prefixed (OGF* — OrcaFilamentLibrary mirrors) + 211 + GF<x>##_## (Afinia/Tiertime suffix) + other GF-shaped + 43 + 55 + + +
+ Bambu's space, used outside Bambu + invented, unowned + deliberate Orca convention +
+
+ +

How it got here

+
+ convention timeline + + + + + + + + + + 2025-01 + OrcaFilamentLibrary + created + + 2025-03 + OGF* prefix born + direct commit, no rationale + + 2025-05 + “≤ 8 chars” check + BBL-only · PR #9574 + + 2025-06 + _## suffix invented + PR #9739, imitated since + + 2025-11 + profile wiki removed + its example taught copy-paste + + 2026-06 + validator -f · BBL → 0 + PR #14459 + + now + this plan + + +
+ +

Beyond the validator's reach

+

The -f check compares a printer only against its own vendor's filaments. Three further ledgers exist:

+
+
16
OFL-internal ids covering several materials — visible on every printer (e.g. OEPLAB00 = 14 Elegoo PLA products)
+
42
OFL×vendor same-id pairs — 32 already neutralized by alias shadowing, 10 live
+
67
ids meaning different materials in different vendors (GFU99 also covers a PEBA; Anycubic's GFL95 “Matte” ≠ Bambu's GFL95 “High Speed”)
+
+
+ + +
+
§3 the rule
+

Nobody invents ids. Structure carries the rest.

+

Chosen by a 3-design → 2-judge → 3-attacker adversarial process. Deterministic minting won on + ambiguity-prevention, contributor simplicity and enforceability; every “breaks” finding from the attack pass is + folded in below.

+ +

The one-question test

+
+ decision — same id or new id? + + + + + + + + Would a user call this a different spool product + than anything already in the tree? + + + + + + + YES — polymer, sub-brand, + fiber-filled, 2nd diameter + NO — same spool, tuned for + another printer / nozzle + it's a GENERIC + material + + + NEW FAMILY + create «Family @base», not instantiated + write NO filament_id anywhere + run the mint script — or paste the id + that CI prints for you + + JOIN THE FAMILY + inherit the existing @base + never write the filament_id key + keep compatible_printers disjoint + from the other variants + + JOIN THE OFL FAMILY + inherit «Generic X @System» + KEEP the «Generic X» name/alias + (alias shadowing then hides the OFL + preset on your printers) · no id key + color → never a new id · “high-speed” for a different printer → same family + “high-speed” selectable alongside the normal preset on one printer → new family + second selectable diameter on the same printer → sibling family with its own id + +
+ +

Minted ids — the setting_id precedent, applied to families

+
+ id anatomy — the new namespace + + + OF + q3xT9k + + + + + + + + + OF — Orca Family namespace + disjoint from GF · QD_ · P· at two chars + base62_6( uuid5( NS, "filament_family/<Vendor>/<Family>" ) ) + e.g. filament_family/Elegoo/Elegoo PLA Matte → OFq3xT9k + same derivation as setting_id · 8 chars total (AMS limit) + + + same input → same id: + no “next number” races · a fork mints the id upstream expects · CI recomputes and verifies + renamed family? + the id does not change — immutable once shipped; renamed_from covers the name + + +
+

Author workflow is two lines: commit the family with no id anywhere; run + python scripts/assign_filament_ids.py (or read the id CI prints in its failure message and paste it). + A --mint "<Vendor>/<Family>" one-shot prints an id without touching the tree.

+ +

Structure: the id lives in exactly one place

+
+ family shape + + + Elegoo PLA Matte @base + instantiation:false · filament_id: "OFq3xT9k" ← only here + + + + + + …@Centauri Carbon + inherits @base · no id key + compat: {CC 0.4} + + …@CC 0.2 nozzle + inherits @base · no id key + compat: {CC 0.2} + + …@Neptune 4 + inherits @base · no id key + compat: {N4, N4 Pro} + + disjoint compatible_printers = the PR #14459 invariant, by construction + family identity is DECLARED — an optional "filament_family" key on the root overrides name derivation + multi-root families are legal (Qidi's per-series bases) — every root must declare the identical id + +
+ +

Reserved namespaces — never mint or hand-write into

+
+ the id space, partitioned + + + + + + + + + GF* + Bambu AMS / RFID — frozen + + QD_* + Qidi device — frozen + + P[0-9A-Fa-f]{7} · "null" + user-custom presets + + OGF* + legacy shapes + grandfathered, closed + + OF[0-9A-Za-z]{6} + open — minted only + + Byte-copies of authentic Bambu ids stay legal where the checked-in shared_catalog sanctions the family (BBL, OFL, Fiberon). + Everything already shipped and unambiguous is snapshot-frozen exactly as-is — the migration touches only colliding families. + +
+
Accepted trade-off: minted ids are opaque — OFq3xT9k doesn't say “PLA”. + The family name sits in the same file; in exchange there is no registry, no “next free number” ceremony, no PR races, + and forks mint the same id upstream expects. The mnemonic registry-grammar runner-up is preserved in §8 if you prefer it — + everything else in this plan works under either format.
+
+ + +
+
§4 error taxonomy — goal 2
+

Eleven patterns explain all 356 groups

+

Classified group-by-group against the actual profile files, then independently spot-checked + (1 substantive disagreement in 14 samples, corrected). The task's two known patterns cover about half; the rest + are new findings.

+ +
+ what's actually wrong — classified groups + + + P1 copy_paste_id — different materials share an id verbatim + 167 + P2 wrong_inherits / id-less product line + ~87 + P3 generic_family_overlap — branded preset rides a generic id + 68 + P4 overclaim_compat — broader variant claims a dedicated printer + 24 + other — deliberate coexistence, hygiene, one true duplicate + 21 + + +

Fixes across all groups: 319 × mint a fresh id · 21 × trim compatible_printers · + 21 × split compat-or-id · 5 × re-point inherits · 1 × merge. Which preset keeps a contested id follows a + mechanical precedence: Bambu-catalog material → OFL generic family → historical first owner.

+
+ +
+
P1copy_paste_id~167
+

The id came along when a profile was copied. Qidi stamped GFB99 into 317 files; + Peopoly put PLA-Silk's id on ABS.

+
fix — impostor families get minted ids on their roots; the owner keeps the id.
+
P2wrong_inherits / no root~87
+

A product line never got its own @base — every variant inherits another family's id. + Flashforge's umbrella collapses ~140 presets into one id.

+
fix — create per-family roots with minted ids; re-point inherits. Never delete the old + shadow files — convert them (they carry real config).
+
P3generic_family_overlap~68
+

A vendor-branded filament rides a generic family id via inheritance, colliding with the true generic + on the same printer.

+
fix — branded → minted id. True generic tunings instead join the OFL family with matching alias. + Sovol is the elegant case: just delete its wrong id lines and inherit OFL's.
+
P4overclaim_compat~24
+

Same material; the broad variant claims a printer that a dedicated variant covers — the BBL H2DP pattern + from PR #14459.

+
fix — trim the broader preset's compatible_printers. Ids untouched.
+
P5template-carried idin P1/P2
+

The id sits on a shared settings template (fdm_filament_*), so every family that inherits + the template collapses onto it (Prusa, Ginger Additive).

+
fix — move ids off templates onto family roots.
+
P6OFL-internal collision16 ids
+

One OFL id spans several materials — and OFL presets are visible on every printer. + OGFL06 = eSUN PLA-Marble and Fiberon PETG-ESD.

+
fix — fix OFL first: it's the base bundle every vendor resolves against.
+
P7alias-mismatch re-exposure10 live
+

A vendor tunes a generic but renames it; alias shadowing fails and the OFL preset resurfaces beside it + (Snapmaker PolyTerra J1 PLA).

+
fix — rename-to-alias where it's genuinely the same family, else mint.
+
P8per-variant ids~210 presets
+

The opposite failure: every nozzle variant has its own id (Prusa, SeeMeCNC, Afinia _##). + No ambiguity — but devices can't recognize the material across nozzles.

+
fix — grandfather (they're unambiguous); converge opportunistically; document as anti-pattern.
+
P9format violationshygiene
+

Ids with spaces ("GFPLA Silk"), 11-char ids, invented GF-shaped ids colliding with + Bambu's real allocations.

+
fix — disappears as a byproduct of re-minting; the unambiguous rest is snapshot-frozen.
+
P10cross-vendor semantic67 ids
+

Same id, different material, different vendors — feeds wrong name/type/temperature data to the globally-matching + consumers (§1).

+
fix — mostly eliminated by P1–P3 re-minting; benign same-material generic sharing is legalized + by the snapshot.
+
P11deliberate coexistence4
+

Snapmaker “Benchy” demo presets coexist on purpose (gated by compatible_prints, which the id check + can't see); one preset lists the same printer three times.

+
fix — mint ids for demo presets; de-duplicate list entries; add a lint.
+
+
+ + +
+
§5 migration plan
+

Fresh ids only, family-atomic, names never change

+

The persistence analysis makes one strategy provably safe — and everything else forbidden.

+ +

What survives an id change

+ + + + + + + + + + +
surfacebehavior across releasesverdict
User presets that inherit a system presetfilament_id re-derived from the parent on every load Preset.cpp:1658-1682safe
Saved 3mf projectspresets resolve by name + config equality, never by id Preset.cpp:2490-2576safe
Klipper / Creality / Snapmaker synctray ids derived at runtime from the installed bundlesafe
User root custom presetscopied a system id at creation and persist it forever — AMS auto-match falls back to generic-by-typelow residue
Qidi QD_* idsencode the device protocol QidiPrinterAgent.cpp:146 — an exact-match contractfrozen
BBL GF* idslive on RFID tags and printer-side records; hardcoded k-values in C++frozen
Recycling an old id for a different materialstale ids in user roots / old 3mfs would silently match the wrong materialforbidden, forever
Renaming / deleting preset namesuser presets whose inherits no longer resolves are dropped at load Preset.cpp:1687-1691forbidden (use renamed_from if unavoidable)
+ +

Phases

+
+
0
+

Tooling + snapshot — no profile changes

+

Land scripts/assign_filament_ids.py, the extended CI checks in ratchet mode, the validator OFL + cross-check and the runtime warning. Generate filament_id_snapshot.json (id→families multimap over main) + and an empty retired_ids.json; both are checked in and append-only.

+
+
1
+

OrcaFilamentLibrary first

+

OFL is the base bundle every vendor resolves against. Fix its 16 internal collisions and give + Generic PETG HF/PETG-CF/PP-CF/… @System their own family roots instead of collapsing into their + parent generic. Unambiguous ids stay byte-identical.

+
+
2
+

Per-vendor PRs, worst first — each widens the CI ratchet

+

Qidi → Flashforge → Elegoo → Prusa → Cubicon → Anycubic → InfiMech → Snapmaker → long tail (22 vendors, + mostly 1–8 one-line fixes). Each merged PR appends its vendor to the workflow's -f -v scope + (today: -v BBL -f); when all are in, drop -v and run tree-wide.

+
+
3
+

Delete the allowlists

+

Once tree-wide zero holds, the ratchet allowlists disappear and every check becomes a hard rule for anything + born after the snapshot.

+
+
+ +
+ where the work is — collision groups per vendor + + + Qidi97 + Flashforge69 + Elegoo37 + Prusa31 + Cubicon17 + Anycubic14 + InfiMech14 + Snapmaker11 + Artillery8 + Creality8 + FlyingBear8 + Sovol6 + 18 more40 total (Volumic 6 · Ratrig 5 · Chuanying 4 · Dremel 3 · 4×2 · 10×1) + + +

Vendor observations show these are systematic mistakes, not hundreds of independent bugs: Qidi is one + blanket-stamp in three profile generations; Elegoo is a single inherit-the-class-base habit; Sovol is fixed by deleting + six wrong lines.

+
+ +

The migration script's contract (hardened by the adversarial pass)

+ + + + + + + +
rulewhy
Re-mint everything — id literals in analysis notes are ignored; only group membership, fix category and keep-id precedence are consumed. Assert no emitted id matches ^(GF|QD_|P[0-9A-Fa-f]{7}$).~40% of classification notes suggested ids in the old invented-GF culture; CI would reject them.
Family-atomic — re-idding any preset re-ids every same-family sibling in the same commit, even outside the collision group.Otherwise a family splits across two ids (FlyingBear GFB99 @S1 vs @Ghost7) — worse than the bug being fixed.
Bounded diff — a vendor PR may only touch collision-group files + same-family siblings; every id that is unambiguous today stays byte-identical.Makes each PR mechanically auditable.
Config-equivalence gate — flattened effective config of every preset must be identical before/after, except the prescribed id/inherits/compat edits.Makes structural conversions provably behavior-neutral (Flashforge's id-less shadow files become named family roots carrying their config byte-for-byte).
No deletions, no renames — redundant twins are re-minted, not dropped; consolidation via renamed_from is a separate human-reviewed cleanup.Deleting a name silently drops users' derived presets at load.
+
+ + +
+
§6 enforcement
+

Ratchets, not absolutes

+

The grandfathered landscape keeps today's benign sharing legal; the checks forbid anything new. + All checks import the same mint function — the setting_id precedent.

+ + + + + + + + + + + +
#checkwhere
1Format — valid iff OF[0-9A-Za-z]{6} or in the snapshot or vendor==BBL or QD_* in Qidi. The legacy set is closed.orca_extra_profile_check.py
drop BBL-only gate :292, OFL skip :601
2Uniqueness ratchet — tree-wide id→families multimap; no id may gain a family claim not recorded in the snapshot (shared_catalog is the sanctioned exception).
3Structure ratchet — id key only on roots; every instantiated preset resolves an effective id (simulated loader walk); all family members resolve identically; no preset may override its inherited id (the Generic SBS drift class).
4Mint conformance — a new id must equal the mint (or a salt iteration); the failure message prints the expected value so hand-editors can paste it.
5Stability — an (id, family) pair on main may not change or vanish, following renamed_from chains, unless listed in a maintainer-gated migrations file. Retired ids go to an append-only ledger and are never redefined.
6Alias hygiene — a preset inheriting an OFL Generic * @System (no own id in its chain) must keep the OFL base name and non-empty compatible_printers; the error names the rename as the cause.
7OFL cross-check — extend check_duplicate_filament_subtypes to include OFL presets in every vendor's per-printer check, minus alias-excluded ones. m_excluded_from is already populated in validator context, so the 32 shadowed pairs won't false-positive and the 10 live ones are caught. Widen CI's -f per vendor.C++ validator
PresetBundle.cpp:5654 · :2302
8Runtime backstop — log a warning when the AMS-sync find_if sees 2+ compatible presets for one id. The only layer that can see side-loaded and forked bundles. One line.runtime
PresetBundle.cpp:3132/3233
+ +

Plus: the rule document ships in-repo (doc/developer-reference/filament_id.md) so CI messages + have a stable link, and the profile-PR template gets one checkbox: “new materials: no filament_id key anywhere — + CI prints the minted id.”

+
+ + +
+
§7 evidence
+

How this was verified

+

The analysis was run as a 52-agent pipeline over the real tree and real git history, with an + adversarial design phase — findings were attacked before being adopted.

+ +
+ methodology + + + + + + + + + 5 code analysts + runtime · persistence · loader + Bambu grammar · git history + + 38 classifiers + every collision group, read + from the actual profile files + + ground truth + audit script = validator + 1256 = 1256 · exact + + + 3 designs + deterministic mint + registry grammar + structure-only + + + 2 judges + both chose the mint + 58 / 50 / 56 + 60 / 54 / 56 + + + 3 attackers + 35 scenarios + authors · runtime · + migration mechanics + every “breaks” → + an amendment §3–§5 + spot-check: 1/14 + + + + + + + + + +

Amendments that came out of the attack pass: family identity declared on roots (not name-derived — + Afinia PLA@HS has no space before @); multi-root families legalized; checks 2–3 as + ratchets, not absolutes; the no-deletion rule; shadow-file conversion instead of deletion; classification id + literals quarantined; diameter siblings; case-insensitive reservation of the user id space.

+
+ +
+ Key discoveries that reshaped the design (with sources) +
+

· The “26 Flashforge presets with no id” were a false alarm — the loader resolves them through the OFL base-bundle + fallback to OGFL99/OGFG99 PresetBundle.cpp:4904-4909. The real bug is + ~10 different Flashforge PLA products sharing one id.

+

· A missing id can never ship: it's a hard load error that discards the whole vendor bundle + PresetBundle.cpp:5072, :5141.

+

· Qidi's QD_* ids are a device-protocol contract, discovered in + QidiPrinterAgent.cpp:146-152 — a second frozen namespace nobody had documented.

+

· User-custom ids occupy P + 7 hex CreatePresetsDialog.cpp:533, and + "null" is a sentinel — both reserved.

+

· OFL shadowing is keyed by alias, not id Preset.cpp:3684-3714, and is already active in + validator context PresetBundle.cpp:2302 — which is what makes check #7 precise.

+
+
+
+ + +
+
§8 open decisions
+

Five calls that are yours to make

+

Everything else in the plan holds under any of these answers. marks the recommendation.

+ +
+
D1id format
+
Opaque deterministic mint OFq3xT9k — no registry, no races, fork-friendly; judges 2/2.
+
Mnemonic registry grammar <NS><FAM><SEQ> — readable ids, but someone allocates numbers forever.
+
D2multi-vendor brands
+
shared_catalog list — sanction authentic Bambu ids for byte-matching families (Snapmaker's Fiberon), low churn.
+
Hoist those families into OFL — cleaner long-term, more restructuring now.
+
D3demo presets
+
Mint ids for Benchy-style presets — simple, keeps the validator strict.
+
Teach the validator compatible_prints gating — more machinery for 3 presets.
+
D4the 10 live OFL duplicates
+
Rename-to-alias where it's truly the same family (with renamed_from) — restores shadowing.
+
Mint vendor ids everywhere — safer mechanically, more ids in the world.
+
D5where the rule doc lives
+
In-repo doc/developer-reference/filament_id.md — CI messages need a stable link.
+
Wiki-only — discoverable, but drifts from the checks that enforce it.
+
+ +
+ Suggested first step: land phase 0 (tooling + snapshot, zero profile changes). It is entirely additive, + makes every later PR mechanically checkable, and turns the 356-group backlog into a shrinking allowlist that CI + reports on every profile PR. +
+
+ +
+ + + + diff --git a/filament_id_plan.md b/filament_id_plan.md new file mode 100644 index 0000000000..7dc4b43d04 --- /dev/null +++ b/filament_id_plan.md @@ -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 = 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`: 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 ` @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//" ) ) +``` + +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 "/"` 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 (`` + 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` (`EB00`); 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. diff --git a/task.md b/task.md new file mode 100644 index 0000000000..b563759767 --- /dev/null +++ b/task.md @@ -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