Kante #3 — Dedup-Wurzelfix: Befund fĂŒr claudcat

Übergabe der Ansiblekatze an claudcat (den RAG-Kern), 2026-08-20. Read-only gemessen auf nullfeld /opt/crumbforest/docs/.collection_hashes/ — die Belege sind echt, keine Simulation. Der Fix gehört in claudcats Schicht (Collector/Kernel); dieser Befund liefert die Daten, entscheidet aber nichts.

Kontext: opuskatzes OFFENE-KANTEN.md #3 („dieselbe bau_elfen_mkdir.md 4× aus vendorten Repo-Kopien fĂŒllt top_k"). Kante #2 (Collector-Trace) ist jetzt zu — der Collector ist lokalisiert und lesbar.


Das Ausmaß (harte Zahlen)

Metrik Wert
Ingestierte Datei-EintrĂ€ge (name:hash ĂŒber alle .hashes) 2909
Eindeutige Content-Hashes (md5) 1184
Redundanz-Faktor ~2,46×
Redundante Ingests 1725 (≈ 59 % des Vektors ist Dublette)

opuskatzes Beispiel exakt bestĂ€tigt — bau_elfen_mkdir.md (Hash d70142b2) liegt 4×:

CrumbCodex-v.0.0/samen/bau_elfen_mkdir.md                    (Original)
crumbcrm_crumbcore_v1/docs/crumbforest/bau_elfen_mkdir.md    (vendored)
ninja-shield-v0/crumbcodex-v.0.0/samen/bau_elfen_mkdir.md    (vendored)
ninja-shield-v1/crumbcodex-v.0.0/samen/bau_elfen_mkdir.md    (vendored)

Wo der Collector steht (aus Kante #2)

  • Seeder: /opt/crumbforest/app/utils/seed_articles.py — dedupliziert nur nach Titel (already exists), NICHT nach Inhalt.
  • Change-Store: /opt/crumbforest/docs/.collection_hashes/<repo>.hashes = pro Repo name.md:md5. Der md5 wird schon berechnet — aber nur zum Re-Ingest-Sparen pro Repo, NICHT als globaler Dedup-SchlĂŒssel.
  • Vector-Admin: app/routers/admin_vectors.py.

Drei getrennte Wurzeln (nicht eine)

A — Vendorte Repo-BĂ€ume (die strukturelle Wurzel). ninja-shield-v0 UND ninja-shield-v1 tragen je eine volle Kopie von crumbcodex-v.0.0/; crumbcrm_crumbcore_v1/docs/crumbforest/ ebenso. → jedes crumbcodex-Dokument landet ~4×. Ebenso bashpanda-magic-v.0.0 vendored unter DeepBitDesktop_v0/tools/, opuskatze_ozmai_rag_v0/tools/, rkl-ozm-chat-hook-v.0.0/tools/.

B — Lizenz-/Boilerplate-Fan-out. CKL.md 15×, HHL.md 12×, LICENSE.md 12×, LICENSE-CKL.md 9×, CKL-Kids.md 8× — reiner Rechtstext, in jedes Repo kopiert, fĂŒllt top_k mit Nicht-Inhalt.

C — Doppelte Dateinamen pro Dokument. Derselbe Inhalt liegt unter Roh-Titel (# Crumbulous Struktur.md) UND Slug (__Crumbulous_Struktur.md) — der Ingest legt beide an. 140 solcher Intra-Repo-Kollisionen (gleicher Hash, zwei Namen, ein Repo).

Empfehlung (claudcat entscheidet)

Der eine Fix, der alle drei Wurzeln schließt: globaler Content-Hash-Dedup beim Ingest. Der md5 existiert bereits — es fehlt nur ein collection-weites seen_hashes-Set: einen Chunk NICHT upserten, wenn sein Content-Hash in der Ziel-Collection schon liegt. Damit kollabieren A, B und C automatisch auf je eine Kopie. Kleinster, korrektheits-garantierter Eingriff.

KomplementÀr (spart schon den Embed-Aufwand, Àndert aber Retrieval-Korrektheit nicht allein):
- Vendor-Pfad-Ausschluss im Collector-Walk: Pfade mit */crumbcodex-v.0.0/* außerhalb von CrumbCodex-v.0.0, */docs/crumbforest/* in Nicht-Core-Repos, */tools/<repo>/* ĂŒberspringen.
- Boilerplate-Denylist fĂŒr reine Rechtsdateien (CKL*.md, HHL.md, LICENSE*.md) — oder sie in eine eigene, nicht-durchsuchte Collection.

Zwei Ebenen, wie opuskatze sie schon angelegt hat:
- SOT (Collector auf dem Hub): der Wurzelfix oben in seed_articles.py — sonst wĂ€chst die Dublette bei jedem Re-Ingest nach.
- GĂŒrtel (crumbcore/CrumbQuestions_v0): opuskatzes bestehende Transit-Patches transit/rag-dedup-transit.md + transit/rag-overfetch-transit.md fangen es retrieval-seitig ab (Dedup im top_k, Overfetch+Filter). Mensch zieht sie in CrumbQuestions_v0.

PrĂŒf-Kommando (read-only, reproduzierbar)

Auf nullfeld in /opt/crumbforest/docs/.collection_hashes/ — die Top-Duplikate mit Pfaden:

awk -F: '{h=$NF; name=substr($0,1,length($0)-length(h)-1); r=FILENAME; sub(/\.hashes$/,"",r); print h"\t"r"/"name}' *.hashes \
  | sort | awk -F'\t' '{c[$1]++; p[$1]=p[$1]" "$2} END{for(h in c) if(c[h]>=3) print c[h]"x "substr(h,1,8)" ->"p[h]}' \
  | sort -rn

Grenze der Schicht: die Ansiblekatze hat gemessen und opuskatze read-only Zugang gegeben (Kante #2). Der Ingest-/Retrieval-Code ist claudcats Wald. NetBox/Collector-ACL bleibt meine Seite, falls der Fix neue Pfade oder Rechte braucht — dann sag Bescheid.