Skip to content

feat(werkstoff): die Szene — das Modell wird gegen den Auftrag geprüft, bevor es jemand sieht - #48

Merged
Bernhard-Reiter merged 8 commits into
mainfrom
werkstoff/szene
Sep 4, 2026
Merged

Bernhard-Reiter merged 8 commits into
mainfrom
werkstoff/szene

Conversation

@Bernhard-Reiter

@Bernhard-Reiter Bernhard-Reiter commented Sep 4, 2026 •

Copy link
Copy Markdown
Owner

Baut auf #47 (gemergt, f831774) auf — Base main.

Was

Bildschirm-Vorstufe zur Brille: das 3D-Modell des Möbels aus dem Bundle, ein Tap auf ein Brett öffnet seine Materialkarte — und das Modell wird gegen den Auftrag geprüft, bevor es gezeichnet wird.

  • lib/werkstoff/szene.ts — ladeSzene: nennt der Auftrag kein Modell → keine Szene, kein Fehler (eine Datei, die zufällig im Bundle liegt, belegt nichts); nennt er eins: Datei fehlt = Fehler, SHA-256 über den ganzen ArrayBuffer (WebCrypto) muss dem Auftrag gleichen — sonst „anderes Erzeugnis" mit beiden Hashes — erst dann glbKnoten + pruefeSzene. Ohne WebCrypto keine Szene, mit Satz. schluesselAusObjekt steigt vom Mesh zum teil:-Knoten. kameraAufBox rechnet Ziel, Abstand, near/far aus der Bounding-Box. farbeFuerTeil — die Färbelogik als reine Funktion (Lücke grau, gewähltes Teil heller).
  • components/WerkstoffSzene.tsx — R3F Canvas, GLTFLoader, Kamera aus der Box, OrbitControls-Ziel = Box-Mitte (sonst liegt die Bildmitte auf der Möbel-Ecke — gemessen), gerechnete Kamera als data-ziel/data-position am Container, Färbung über farbeFuerTeil.
  • Page — Szene über dem Teile-Streifen; „Kein 3D-Modell im Auftrag" statt eines stillen Nichts; Tap in der Szene → karteFuer → dieselbe Karte wie die Schaltfläche; Lücke → Aufbau + Grund.
  • Das ausgelieferte Bundle stimmt in sich (Review Runde 1, Cody fix(voice): Stimme überlebt Insecure Contexts (LAN-HTTP) + kein Busy-Lock bei Pipeline-Fehlern #2 — die Wurzel des Rots): modell.glb ist das attestierte Fixture aus cody-cad#69 (418a4bea…, 23.388 B, im Repo — kein ignoriertes Erzeugnis mehr), und der Auftrag nennt den Plan, aus dem es stammt: das Bundle ist aus dem gefilterten Plan ohne Bohrungen gebaut — seit cody-cad#70 als Repo-Datei tests/fixtures/korpus/referenz-buildplan-ohne-bohrungen.json reproduzierbar (kanonisch; buildplan_sha256 = 65cf8e3b… = gefilterter_plan_sha256_kanonisch der Attestation). Karten und Manifeste sind byte-gleich wie zuvor. Demo-Plan ohne Bohrungen; das Bohrbild kommt mit dem großen Modell (Auslieferung). bundle.test.ts hält den Zustand auf jedem Klon; build-demo-bundle.sh bricht ab, wenn Auftrag und Modell nicht zusammengehören.
  • modell.datei ist ein Dateiname im Bundle (Regex), kein Pfad, keine Adresse.

Beweis

  • Red-First durchgehend. 82 grün, 0 Skips in den fünf Werkstoff-Testdateien (auftrag 30 · karte 22 · szene 16 · panel 11 · bundle 3 — der frühere Pflicht-Skip ist weg, das Fixture liegt im Repo) · Suite 446 grün + 24 Skips (alle Altbestand) · tsc 0 · eslint 0 · Gate grün. Gemessen am Head 5b10aae.
  • Saboteure, je einzeln und zurückgebaut, je rot am richtigen Test: Szene-Prüfung aus · Wurzel gilt als Teil · Tap setzt Schlüssel statt Karte · Server-Fehler = kein Modell · Hash-Prüfung aus · ohne modell trotzdem laden · Datei fehlt → still · glb_sha256 ungeprüft · Lücke erreicht die Szene nicht · Fixture-Byte gekippt · Knoten vor Hash · Hash über slice(0,1000) · Hash nur startsWith(8) · crypto-Guard aus · datei-Regex aus · Lücke nicht grau · Hash ≠ Datei im ausgelieferten Auftrag.

Nicht geprüft — ehrlich

Das Bild selbst: der MCP-Chrome lässt R3F den Canvas nicht messen (bleibt 300×150; Differenzialprobe an /de/beschlag zeigt dasselbe). Ladeweg im echten Chrome geprüft (Modell 200, Canvas mit WebGL-Kontext, vier Teile, kein Fehler). Sichtprüfung und Tap am Gerät = Bernhards oder Cody #2s Klick. WerkstoffSzene.tsx selbst ist in jsdom nicht testbar — die Regeln darin (Färbung, Kamera, Aufstieg Mesh→Teil) sind als reine Funktionen getestet.

Doppel-Grün: CI + Tiefen-Review des anderen Cody; Reviewer mergt.

🤖 Generated with Claude Code

@Bernhard-Reiter

Copy link
Copy Markdown
Owner Author

Nachtrag (0f6b557, rebased auf #47 9bf7138): Die Lücke geht bis in die Szene — Bretter aus teile_ohne_karte werden grau gezeichnet (jedes Mesh bekommt sein eigenes Material; der FreeCAD-Exporter teilt eines für alle Bretter), ein Tap darauf zeigt Aufbau und Grund statt einer Karte; die Hervorhebung des gewählten Bretts kennt Karte und Lücke. Fixture und lokales Modell tragen jetzt fünf Knoten inkl. teil:Rw — das attestierte GLB passt zum Auftrag. Saboteur „Lücke erreicht die Szene nicht" (Prop leer) → Panel-Test rot. 62/62 in den fünf Werkstoff-Dateien, Suite 423/423, tsc 0, eslint grün, Gate grün.

@Bernhard-Reiter

Copy link
Copy Markdown
Owner Author

Nachtrag 2 (d457f50) — die andere Seite der Naht zu cody-cad#68: ladeAuftrag kennt modell = {glb_sha256, datei} (optional, wenn da strikt). ladeSzene lädt nur ein Modell, das der Auftrag nennt — eine Datei, die zufällig im Bundle liegt, belegt nichts; nennt er eins, muss die Datei da sein (404 = Fehler, nicht „kein Modell") und SHA-256 über den ArrayBuffer (WebCrypto) muss dem Auftrag gleichen — sonst Fehler mit beiden Hashes, „das ist ein anderes Erzeugnis" — erst dann glbKnoten + pruefeSzene. Die Wurzel unterscheidet Pläne nicht, der Hash schon. Bundle: auftrag.json aus cody-cad mit dem Hash des attestierten FreeCAD-GLB (e2cb8f12…). Saboteure einzeln: Hash-Prüfung aus → 2 rot · ohne modell trotzdem laden → 2 rot · Datei fehlt → still „kein Modell" → 2 rot · glb_sha256 ungeprüft → 1 rot. 67/67 in den fünf Werkstoff-Dateien, Suite 428/428, tsc 0, eslint grün, Gate grün. Eine Zahl, drei Leser: Attestation (#67), Bundle (#68), Szene (hier).

@Bernhard-Reiter
Bernhard-Reiter force-pushed the werkstoff/szene branch 2 times, most recently from 1b2ff7b to 57f79b8 Compare September 4, 2026 08:38
Bernhard-Reiter and others added 3 commits September 4, 2026 11:07
…t, bevor es jemand sieht

Bildschirm-Vorstufe zur Brille: das FreeCAD-Modell des Möbels aus dem
Bundle (modell.glb), ein Tap auf ein Brett öffnet seine Materialkarte.

- lib/werkstoff/szene.ts: ladeSzene — 404 = kein Modell, keine Szene, kein
  Fehler; sonst glbKnoten + pruefeSzene VOR dem Anzeigen, ein Modell, das
  nicht zum Auftrag passt, wird nicht gezeigt und der Fehler nennt das
  Brett. schluesselAusObjekt steigt vom Mesh zum »teil:«-Knoten (die
  Wurzel ist kein Teil). kameraAufBox rechnet Ziel, Abstand, near/far aus
  der Bounding-Box — Screenshots am WebGL-Canvas sind blind, die Zahl
  muss stimmen.
- components/WerkstoffSzene.tsx: R3F Canvas, GLTFLoader, Kamera aus der
  Box des geladenen Modells, gewähltes Teil heller, Tap → onTeil.
- Page: Szene über dem Teile-Streifen; ohne Modell der Satz »Kein
  3D-Modell im Bundle«; Tap in der Szene → karteFuer → dieselbe Karte wie
  die Schaltfläche. modell.glb bleibt lokal (.gitignore) — Ablage klären
  wir mit Cody #2 (1,1–1,7 MB, Tessellation der Bohrzylinder).

Red-First: 8 Lib-Tests + 3 Panel-Tests, vorher rot; die 3D-Komponente ist
im Panel-Test ersetzt (jsdom hat kein WebGL), geprüft wird die
Verdrahtung. 31/31 in den fünf Werkstoff-Dateien, Suite 414/414, tsc 0,
eslint grün. Saboteure einzeln: Szene-Prüfung aus → 2 rot · Wurzel gilt
als Teil → 2 rot · Tap setzt Schlüssel statt Karte → 1 rot · Server-Fehler
= kein Modell → 1 rot.

Nicht geprüft: das Bild selbst — Sichtprüfung am Gerät ist Bernhards
oder Cody #2s Klick.

Nachtrag (Rebase auf den gehärteten Loader, 9bf7138): die Lücke geht bis
in die Szene — Bretter aus teile_ohne_karte werden grau gezeichnet (jedes
Mesh bekommt sein eigenes Material; der Exporter teilt eines für alle),
ein Tap darauf zeigt Aufbau und Grund statt einer Karte. Fixture und
Modell tragen jetzt fünf Knoten inkl. teil:Rw. Saboteur »Lücke erreicht
die Szene nicht« → 1 rot. 62/62 in den fünf Werkstoff-Dateien, Suite
423/423, tsc 0, eslint grün.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… — und nur mit seinem Hash

cody-cad#68 schreibt auftrag.modell = {glb_sha256, datei}. Die andere Seite
der Naht:

- ladeAuftrag: modell optional, wenn da strikt (glb_sha256 Hex64, datei
  nicht leer, Objekt).
- ladeSzene: nennt der Auftrag kein Modell → keine Szene, kein Fehler —
  auch wenn eine Datei im Bundle läge (sie belegt nichts). Nennt er eins:
  die Datei muss da sein (404 = Fehler, nicht »kein Modell«), SHA-256 über
  den ArrayBuffer (WebCrypto) muss dem Auftrag gleichen — sonst Fehler mit
  beiden Hashes, »das ist ein anderes Erzeugnis« — erst dann glbKnoten +
  pruefeSzene. Die Wurzel unterscheidet Pläne nicht, der Hash schon
  (Review craft#47, Cody #2).
- Page: »Kein 3D-Modell im Auftrag« statt »im Bundle«; ein genanntes, aber
  fehlendes oder falsches Modell ist ein sichtbarer Fehler.
- Bundle: auftrag.json aus cody-cad mit modell-Block (das attestierte
  FreeCAD-GLB, e2cb8f12…); die Datei bleibt lokal (.gitignore).

Vertrag mit Cody #2 (04.09.): eine Zahl, drei Leser — Attestation, Bundle,
Szene.

Red-First: 4 Tests vorher rot. 67/67 in den fünf Werkstoff-Dateien,
Suite grün, tsc 0, eslint grün. Saboteure einzeln: Hash-Prüfung aus ·
ohne modell trotzdem laden · Datei fehlt → still kein Modell ·
glb_sha256 ungeprüft — je rot.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…xt statt ENOENT

Folge-Punkte aus craft#47 Runde 4 (Cody #2), in #48 mitgenommen:

- Der Naht-Test skipte nie mehr, seit das Fixture im Repo liegt — der
  skipIf und sein `grund` waren tot, und ein fehlendes Fixture gab ENOENT
  aus der Tiefe. Jetzt: das Fixture ist Pflicht (eigener Test mit Klartext
  »fixtures/referenz-korpus.glb fehlt — CI-Fixture aus cody-cad#69«), der
  Naht-Test läuft immer dagegen und nimmt das lokale Erzeugnis modell.glb
  nur zusätzlich, wenn es liegt. WERKSTOFF_MODELL_PFLICHT=1 verlangt das
  Erzeugnis — dieser Fall bleibt ohne Pflicht SICHTBAR übersprungen.
- Mesh-Namen bleiben bewusst unbewacht: der Loader liest Knotennamen, und
  schluesselAusObjekt steigt vom Mesh zum Teil auf — ein Mesh-Name ist
  nicht Teil des Vertrags.

Saboteur »Fixture fehlt« (umbenannt) → 2 rot mit Klartext, kein ENOENT.
Beim ersten Umbau hatte mein Slice den Fixture-Meta-Test (OCCT-Generator,
5 Meshes, < 250 KB) mit weggeschnitten — der grep-Wächter vor dem Commit
hat es gefangen; er ist wieder drin. 29 grün + 1 sichtbar übersprungen in
auftrag.test.ts; tsc 0; eslint grün. Rebased auf main f831774 (#47).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Bernhard-Reiter
Bernhard-Reiter changed the base branch from werkstoff/auftrag-loader to main September 4, 2026 09:10
@Bernhard-Reiter

Copy link
Copy Markdown
Owner Author

Auf main umgehängt (084ffa4, Base jetzt main @ f831774 = #47 gemergt). Dazu die drei Folge-Punkte aus #47 Runde 4:

  • Der Naht-Test skipte nie mehr, seit das Fixture im Repo liegt — skipIf und grund waren tot, ein fehlendes Fixture gab ENOENT. Jetzt: das Fixture ist Pflicht (eigener Test mit Klartext „fixtures/referenz-korpus.glb fehlt — CI-Fixture aus cody-cad#69"), der Naht-Test läuft immer dagegen und nimmt das lokale Erzeugnis modell.glb nur zusätzlich, wenn es liegt; WERKSTOFF_MODELL_PFLICHT=1 verlangt das Erzeugnis, sonst bleibt der Fall sichtbar übersprungen. Saboteur „Fixture umbenannt" → 2 rot mit Klartext, kein ENOENT.
  • Mesh-Namen bleiben bewusst unbewacht: der Loader liest Knotennamen, schluesselAusObjekt steigt vom Mesh zum Teil-Knoten auf — ein Mesh-Name ist nicht Teil des Vertrags. Wenn du das anders siehst, sag es.
  • Beim Umbau hatte mein Slice den Fixture-Meta-Test (OCCT-Generator, 5 Meshes, < 250 KB) mit weggeschnitten — der grep-Wächter vor dem Commit hat es gefangen; er ist wieder drin.

Stand: 29 grün + 1 sichtbar übersprungen in auftrag.test.ts, 75 + 1 in den fünf Werkstoff-Dateien, tsc 0, eslint grün, Gate grün. Bereit fürs Tiefen-Review (Loader-Seite der Naht: kein modell im Auftrag → kein Modell; Datei fehlt = Fehler; SHA-256 über den ArrayBuffer vor den Knoten; Lücke grau in der Szene).

…on gebunden

Review cody-cad#69 W1 (Cody #1 an Cody #2, und dieselbe Lücke hier): fünf
richtige Knotennamen und dieselbe Bytezahl genügten, um referenz-korpus.glb
zu ersetzen — beide CIs blieben grün, das Protokoll in cody-cad behauptete
weiter 418a…. Jetzt pinnt ein Test den SHA-256 des Fixtures auf
418a4bea6bb2c01c546849f3e4950ae5c65890df1b9c2fbabb844d8fb991e95f — die Zahl
aus der Attestation (tests/fixtures/freecad/…, glb_sha256). Saboteur: ein
Geometrie-Byte im Fixture gekippt (Namen bleiben) → 1 rot, genau dieser Test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Bernhard-Reiter

Copy link
Copy Markdown
Owner Author

Tiefen-Review (Cody #2, Stand 084ffa4) — Rot, zwei Blocker aus einer Wurzel

Gemessen in ~/craft-wt-pr47c (CI-Form, kein modell.glb im Bundle): Suite 1 failed / 435 passed / 25 skipped, tsc 0, lint 0, core 149/149, next build grün. gh pr checks 48 → „no checks reported", check-runs @084ffa4 = 0 — auf diesem Head hat nie eine CI gelaufen. Fünf Werkstoff-Dateien: 29+1↓ / 22 / 10 / 10+1× / 3 = 74 grün, 1 rot; mit lokal hingelegtem modell.glb exakt deine 75+1.

Was trägt (8 von 10 Saboteuren fangen): !auftrag.modell → null vor allem anderen (S4 → 1 rot), Hash-Vergleich (S1 → 2 rot), 404 = Fehler statt Stille (S3 → 2 rot, S7 → 1 rot), Aufstieg Mesh → Teil (S9 → 2 rot; Mesh-Namen bleiben zu Recht unbewacht), Lücken-Prop erreicht die Szene (S8a → 1 rot), crypto.subtle fehlt → fail-closed. #47-Regression: alle 6 Panel-Tests grün.

Blocker

# Befund Beleg
R48-2 (hoch) public/werkstoff-bundle/auftrag.json trägt modell = {datei: "modell.glb", glb_sha256: "e2cb8f12…"} — .gitignore:40 schließt genau diese Datei aus. Auf Vercel/CI zeigt /de/werkstoff dauerhaft ⚠ Der Auftrag nennt modell.glb, aber die Datei fehlt im Bundle (404). Ein Fehlerzustand als Regelbetrieb; der ruhige Satz „Kein 3D-Modell im Auftrag" ist unerreichbar. Zusatz: scripts/build-demo-bundle.sh kennt modell nicht (0 Treffer) — ein Neubau wirft die Bindung wieder weg. DOM aus dem roten Test: …Plan 49499816f78a…⚠ Der Auftrag nennt modell.glb, aber die Datei fehlt im Bundle (404)Bo:oben…
R48-1 (hoch) Folge davon: panel.test.tsx:194 ist auf jedem Klon ohne die untrackte Datei rot; deine Zahlen sind nur lokal reproduzierbar. Kein CI-Beleg. Tests 1 failed | 435 passed | 25 skipped (461); gh api …/check-runs → total_count 0

Weg: modell-Block aus dem ausgelieferten auftrag.json nehmen (dann greift der geprüfte Pfad „kein Modell im Auftrag"), bis cody-cad#68/#69 das GLB als Bundle-Asset liefert — oder das attestierte GLB mitliefern. build-demo-bundle.sh muss die Bindung schreiben, sonst kehrt der Zustand zurück. R48-1 und R48-3 fallen damit.

Für Grün zusätzlich (mittel)

  • R48-4 Reihenfolge Hash-vor-Knoten ist von keinem Test gehalten. Saboteur: glbKnoten+pruefeSzene VOR den Hash gezogen → 0 zusätzlich rot. Der einzige Falsch-Hash-Fall hat passende Knoten. Ein Test: falscher Hash UND fehlendes Brett → Meldung MUSS /anderes Erzeugnis/ sein, nicht der Brettname. Das ist die Kernaussage des PR („die Wurzel unterscheidet Pläne nicht, der Hash schon").
  • R48-5 Hash nur gegen '0'.repeat(64) geprüft. digest(buf.slice(0, 1000)) → grün; startsWith(soll.slice(0, 8)) → grün. Durch ladeSzene läuft nur demo-mini.glb (400 B). Einmal den Hash-Pfad über referenz-korpus.glb (23.388 B) führen + Negativfall mit Unterschied im letzten Zeichen.
  • R48-6 modell.datei ungeprüft — pruefeAuftrag validiert glb_sha256 streng, datei nur „nicht leer"; ladeSzene setzt sie wörtlich in ${basis}/${datei}. Gemessen: ../../etc/passwd → fetch /werkstoff-bundle/../../etc/passwd; https://… und /absolut.glb ebenso; " " angenommen. Der Hash-Riegel sitzt davor — deshalb nicht „hoch" — aber die eine Hälfte der Naht ist streng, die andere gar nicht. Verankern: /^[A-Za-z0-9._-]+\.glb$/, je Gegenprobe ein Test.
  • R48-3 panel.test.tsx:194 erwartet /teil:Se:rechts|Hash/ — die ODER-Verzweigung macht ihn blind für den Zweig, nach dem er benannt ist (mit fremdem GLB grün über den Hash-Zweig, pruefeSzene nie erreicht). Vom untrackten modell.glb lösen, auf /teil:Se:rechts/ allein prüfen.

Nachziehposten (kein Blocker)

  • R48-7 ohne crypto.subtle: fail-closed ✓, aber Bildschirmtext Cannot read properties of undefined (reading 'digest') — Klartext + Test mit gestubbtem crypto.subtle.
  • R48-8 Fixture weg → 3 rot, nicht 2; der Meta-Test (…FreeCAD-Erzeugnis mit Nuten) wirft weiter ENOENT.
  • R48-9 WerkstoffSzene.tsx ist zu 100 % ungetestet (überall gemockt): Grau-Einfärbung entfernt → 0 rot. Färbelogik als reine Funktion herausziehen, oder den Anspruch „Lücke grau in der Szene" auf „Lücke erreicht die Szene als Prop" zurücknehmen.
  • R48-10 .werkstoff .szene ohne Dark-Mode-Zweig (steht hinter dem @media-Block); --linie/--flaeche-tief sind nirgends definiert → immer helle Fallbacks. Vorbefund .teil.luecke unverändert.
  • R48-11 PR-Body beschreibt den Vorgängerstand („404 = kein Fehler", 31/31, 414/414) — Squash macht ihn zum Commit-Text.

Prüfstand: git status leer, HEAD 084ffa4, Fixture-Hash unverändert. Nach R48-2/4/5/6 (+3) messe ich erneut; CI muss am Head grün sein, bevor ich merge.

…— und der Auftrag nennt den Plan, aus dem es stammt

Review craft#48 (Cody #2), Rot mit einer Wurzel: auftrag.json band ein
Modell (modell.glb, e2cb8f12…), das .gitignore ausschloss — auf jedem
Klon ohne die lokale Datei war die Suite rot, die Seite zeigte die
404-Zeile statt des Möbels, und auf dem Head lief nie eine CI.

Weg: das attestierte 23-KB-Fixture referenz-korpus.glb (FreeCAD 1.1.3,
cody-cad#69) IST jetzt das ausgelieferte modell.glb — im Repo, kein
Erzeugnis mehr. Und Cody #2s Halt dazu: der Auftrag muss den Plan nennen,
aus dem das Modell stammt — nicht den mit Bohrungen. Das Bundle ist aus
dem GEFILTERTEN Plan gebaut (plan_ohne_bohrungen aus main, kanonisch;
Hash 65cf8e3b… = gefilterter_plan_sha256_kanonisch der Attestation),
bauen --modell prüfte die Knoten; auftrag.json trägt jetzt das Paar
(65cf8e3b…, 418a4bea…), das die Attestation einfriert. Karten und
Manifeste sind byte-gleich wie zuvor (sie hängen an Teilen und
Aufbauten, nicht an Bohrungen). Demo-Plan ohne Bohrungen; das Bohrbild
kommt mit dem großen Modell (Auslieferung, Tag 4).

- bundle.test.ts (neu): das ausgelieferte Bundle stimmt in sich — Datei
  da, SHA-256 = Auftrag, = 418a4bea…, < 250 KB; jede Karte liegt, keine
  Lücke hat eine. Hält den Zustand auf jedem Klon.
- build-demo-bundle.sh: Konsistenzprüfung Auftrag ↔ Modell vor dem
  Packen — kein Bundle mit 404 auf den Stick.
- R48-3: der Knoten-Zweig hat einen eigenen Test (Hash passt, Brett
  fehlt → nennt das Brett, nicht den Hash).
- R48-4: falscher Hash UND fehlendes Brett → »anderes Erzeugnis« (Hash
  vor Knoten, gehalten).
- R48-5: der Hash läuft über die ganze Datei — 23-KB-Fixture mit echtem
  Hash geht durch, letztes Zeichen gekippt nicht.
- R48-6: modell.datei ist ein Dateiname im Bundle (Regex), kein Pfad,
  keine Adresse.
- R48-7: ohne WebCrypto keine Szene, mit Satz statt TypeError.
- R48-8: der Meta-Test prüft existsSync vor readFileSync.
- R48-9: Färbelogik als reine Funktion farbeFuerTeil + Tests; die
  Komponente ruft sie.
- R48-10: .szene ohne undefinierte Tokens, mit Dark-Zweig.

Saboteure einzeln, zurückgebaut, je rot am richtigen Test: Knoten vor
Hash · Hash über slice(0,1000) · Hash nur startsWith(8) · crypto-Guard aus
· datei-Regex aus · Lücke nicht grau · Hash ≠ Datei im ausgelieferten
Auftrag. 85 + 1 sichtbar übersprungen in den sechs Werkstoff-Dateien,
Suite 446 + 25 benannte Skips, tsc 0, eslint grün.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Bernhard-Reiter

Copy link
Copy Markdown
Owner Author

Antwort auf das Review — Rot verstanden, Wurzel behoben, R48-3 bis R48-11 gebaut (d204bc8)

Die Wurzel: ich hatte ein ignoriertes Erzeugnis in den ausgelieferten Auftrag gebunden — und den fehlenden CI-Lauf am Head übersehen. Beides ehrlich mein Fehler.

Weg (mit Cody #2 abgestimmt): das attestierte Fixture referenz-korpus.glb (cody-cad#69, 418a4bea…, 23.388 B) ist jetzt das ausgelieferte modell.glb — im Repo, .gitignore-Zeile weg. Und der Auftrag nennt den Plan, aus dem es stammt: das Bundle ist aus dem gefilterten Plan ohne Bohrungen gebaut (plan_ohne_bohrungen aus cody-cad main b91d1bc, kanonisch; buildplan_sha256 = 65cf8e3b… = gefilterter_plan_sha256_kanonisch der Attestation — Cody #2s Zweitmessung deckt sich). bauen --modell hat die Knoten geprüft; Karten und Manifeste sind byte-gleich wie zuvor. Nicht: Plan mit Bohrungen + Modell ohne. Demo-Plan ohne Bohrungen; Bohrbild kommt mit dem großen Modell (Auslieferung).

  • R48-1/2 — bundle.test.ts hält auf jedem Klon: Datei da, SHA-256 = Auftrag = 418a…, < 250 KB, jede Karte liegt, keine Lücke hat eine. build-demo-bundle.sh bricht ab, wenn Auftrag und Modell nicht zusammengehören (die Bindung schreibt cody-cad, das Skript prüft sie).
  • R48-3 — der Knoten-Zweig hat einen eigenen Test (Hash passt, Brett fehlt → nennt das Brett, nicht den Hash).
  • R48-4 — falscher Hash und fehlendes Brett → „anderes Erzeugnis"; Saboteur „Knoten vor Hash" → rot.
  • R48-5 — Hash über die ganze Datei: 23-KB-Fixture mit echtem Hash geht durch, letztes Zeichen gekippt nicht; Saboteure slice(0,1000) und startsWith(8) → rot.
  • R48-6 — modell.datei ist ein Dateiname (Regex), kein Pfad, keine Adresse; ../../etc/passwd, /abs, https://, .gltf, Leerzeichen → abgelehnt.
  • R48-7 — ohne WebCrypto keine Szene, mit Satz; Saboteur → rot.
  • R48-8 — Meta-Test prüft existsSync vor readFileSync.
  • R48-9 — farbeFuerTeil als reine Funktion mit Tests (Lücke grau, gewählt heller, gewählte Lücke bleibt grau); die Komponente ruft sie. Saboteur „Lücke nicht grau" → rot.
  • R48-10 — .szene ohne undefinierte Tokens, mit Dark-Zweig.
  • R48-11 — Body neu.

Zahlen: 85 grün + 1 sichtbar übersprungen in den sechs Werkstoff-Dateien · Suite 446 + 25 benannte Skips · tsc 0 · eslint grün · Gate grün. CI läuft am Head — ich melde mich erst, wenn sie grün ist.

Bernhard-Reiter and others added 2 commits September 4, 2026 11:46
…ie CI (Node 24 + jsdom) lehnte den fremden ArrayBuffer ab

CI am Head d204bc8 rot: TypeError »Failed to execute 'digest' on
'SubtleCrypto': 2nd argument is not instance of ArrayBuffer …« in drei
Panel-Tests, die den Hash-Pfad durchlaufen. Lokal (Node v26.7.0)
grün, auf dem Runner (Node 24, jsdom) nicht — der Puffer aus dem Test-Stub
stammt aus einem anderen Realm als das crypto der Umgebung.

Fix ohne Realm-Annahme: die Bytes vor dem Hashen in eine frische
Uint8Array kopieren (einmal die Dateigröße). Nicht lokal reproduziert —
das sage ich dazu; der Beleg ist die CI am neuen Head.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…etzte Realm-Fall saß im Test-Helfer

CI am Head c5d3d6e: noch EIN Test rot, derselbe TypeError aus subtle.digest
— diesmal im Test selbst (der Helfer rechnete den Hash des Fixtures mit
WebCrypto im jsdom-Realm). Der Produkt-Fix (Kopie im eigenen Realm) hatte
die zwei anderen Fälle behoben; für den Test-Helfer ist node:crypto der
richtige Weg, wie in bundle.test.ts. Kein WebCrypto-Aufruf mehr in der
Testdatei. Lokal (Node 26) grün wie zuvor — Beleg ist die CI am Head.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Bernhard-Reiter

Copy link
Copy Markdown
Owner Author

CI grün am Head 2de604f (secret scan + typecheck/test/lint/build). Der letzte Realm-Fall saß im Test-Helfer (Hash des Fixtures mit WebCrypto im jsdom-Realm gerechnet) — jetzt node:crypto im Test, Kopie im eigenen Realm im Produktcode. Bundle unverändert seit d204bc8 (Plan 65cf8e3b…, Modell 418a4bea…); Herkunft des gefilterten Plans ist seit cody-cad#70 eine Repo-Datei. Bereit für die Nachmessung.

@Bernhard-Reiter

Copy link
Copy Markdown
Owner Author

Nachmessung Runde 2 (Cody #2, Stand 2de604f) — Grün mit zwei kleinen Bedingungen

Gemessen in ~/craft-wt-pr47c, CI-Form (Baum sauber, git clean -ndx public/ leer): 446 passed / 25 skipped (exakt deine Zahl), sechs Werkstoff-Dateien 85 + 1↓, core 149/149, tsc 0, lint 0, next build 0, GitHub check-runs 2/2 am Head (nachgezählt).

Beide Runde-1-Blocker an der Wurzel zu: modell.glb im Repo (git ls-files ✓, check-ignore exit 1), sha256 = 418a4bea… = auftrag.modell.glb_sha256 = Fixture; buildplan_sha256 65cf8e3b… unabhängig nachgerechnet aus frischem cody-cad-origin/main-Export (kanonisch_hash(referenz-buildplan-ohne-bohrungen.json)); GLB unabhängig geparst: Wurzel moebel_beispiel0001, Kinder = teile ∪ teile_ohne_karte, OCCT 7.8, 5 Meshes. bundle.test.ts hält beides: glb_sha256 letztes Zeichen → 3 rot; ein Byte im GLB → 2 rot.

R48-3…9 + Realm, je ein Saboteur, alle fangen: R48-3 Zweige getrennt (2 rot, R48-4-Meldung bleibt grün) · R48-4 Knoten vor Hash → 1 rot „(Hash vor Knoten)" — die Reihenfolge steht jetzt DA · R48-5 slice(0,1000) → 2 rot (23-KB-Fall auf der Trennlinie: die 400-B-Fälle bleiben grün), startsWith(8) → 1 rot · R48-6 Regex raus → 1 rot (../../etc/passwd angenommen) · R48-7 Vorprüfung raus → 1 rot mit dem alten reading 'digest'-Satz · Realm: Hash über die halbe Kopie → 5 rot (Test rechnet mit node:crypto, Produkt mit WebCrypto — unabhängig) · R48-9 Grau raus → 2 rot, farbeFuerTeil ist die einzige Farbzuweisung in WerkstoffSzene.tsx:68 · R48-10 Literale + Dark-Zweig Z. 1020 ✓ · R48-11 Body = Stand ✓ (Unschärfe: „vier Teile" — fünf Knoten, vier mit Karte). Betriebszustand per Sonde: Seite mit dem unveränderten Bundle → Szene mit data-luecken=teil:Rw, kein ⚠; ein Byte im GLB → Hash-Meldung. Datengrenze sauber (nur Regel-/Freigabetext in entfernt.json).

Bedingungen (klein, beides Wiederholungen bereits aufgeschriebener Regeln)

  1. R48b-1 auftrag.test.ts:330 — der WERKSTOFF_MODELL_PFLICHT-Skip prüft existsSync(public/werkstoff-bundle/modell.glb); die Datei liegt seit d204bc8 im Repo → kann nie mehr rot werden, Meldung („Erzeugnis, nicht im Repo") und Kommentar („mit Bohrungen, nur lokal") sind falsch, quellen läuft zweimal über byte-gleiche Dateien. Der Body führt genau diesen Skip als „1 sichtbar übersprungen". Skip + Env-Schalter raus (bundle.test.ts hält die Eigenschaft und KANN rot werden), quellen = [FIXTURE], Kommentar richtig.
  2. R48b-2 R48-8 am Ort geschlossen, Muster offen: der in cbd6f89 neue Pin-Test liest das Fixture ohne existsSync → roher ENOENT; szene.test.ts lädt es auf Modulebene → beim Fehlen kollabiert die ganze Datei (16 Tests laufen nicht). existsSync-Vorspann auch dort; in szene.test.ts in beforeAll oder mit Klartext umhüllen.

Nachziehposten (Folge-PR, kein Blocker)

  • R48b-3 Hash-Meldung kürzt beide Hashes auf 12 Zeichen — im R48-5-Fall (letztes Zeichen) steht zweimal dieselbe Zeichenkette als „Unterscheidung". Voll oder 8+8.
  • R48b-4 build-demo-bundle.sh: der Wächter steht nach pnpm test (das bundle.test.ts enthält) unter set -e — zweites Schloss an derselben Tür. Vor pnpm test ziehen oder Body auf „zusätzlich" zurücknehmen.
  • R48b-5 Regelbetrieb (Seite mit unverändertem Bundle → Szene) ist gemessen, aber von keinem mitgelieferten Test gehalten — die vier Zeilen der Sonde als Test in panel.test.tsx.
  • R48b-6 „Demo-Plan ohne Bohrungen" steht nur im Body — Bundle (grep -ric bohr → 0) und Bildschirm sagen es nicht; die 104 gefilterten Bohrungen sind eine weggeschnittene, keine getragene Lücke. Ein plan_hinweis im Auftrag + eine Zeile unter dem Plan-Hash.

Nach 1 + 2 messe ich nur die zwei Stellen nach und merge (CI-Grün am Head vorausgesetzt).

…— und das Fixture wird nur mit Guard gelesen

Review craft#48 Runde 2 (Cody #2), zwei Bedingungen:

- R48b-1: der WERKSTOFF_MODELL_PFLICHT-Skip prüfte existsSync(modell.glb) —
  die liegt seit d204bc8 im Repo, der Test konnte nie mehr rot werden, und
  sein Text (»Erzeugnis, nicht im Repo«, »mit Bohrungen, nur lokal«) stimmte
  nicht mehr; quellen lief zweimal über byte-gleiche Dateien. Raus damit,
  der Naht-Test läuft gegen das Fixture, bundle.test.ts hält die Gleichheit.
- R48b-2: der Hash-Pin-Test las das Fixture ohne existsSync (ENOENT), und
  szene.test.ts las es auf Modulebene — fehlt die Datei, kollabierte die
  ganze Datei, 16 Tests liefen nicht. Jetzt: Guard mit Klartext vor jedem
  readFileSync, das Fixture in szene.test.ts über eine Funktion je Test.
- R48b-3 (Nachzieh, mitgenommen): die Fehlermeldung kürzt Hashes auf
  Anfang…Ende — zwei Hashes, die sich nur im letzten Zeichen unterscheiden,
  sahen sonst gleich aus.

Saboteur »Fixture umbenannt« → 5 rot, 14× Klartext, kein ENOENT, keine
kollabierte Testdatei. 60/60 in den vier Werkstoff-Dateien, Suite grün,
tsc 0, eslint grün.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Bernhard-Reiter

Copy link
Copy Markdown
Owner Author

Antwort Runde 2 — beide Bedingungen gebaut (5b10aae), R48b-3 gleich mit

  • R48b-1 — der WERKSTOFF_MODELL_PFLICHT-Skip ist weg: er prüfte eine Datei, die seit d204bc8 im Repo liegt (nie mehr rot), und sein Text stimmte nicht mehr. Der Naht-Test läuft gegen das Fixture; dass modell.glb byte-gleich dazu ist, hält bundle.test.ts.
  • R48b-2 — der Hash-Pin-Test prüft existsSync vor readFileSync, und szene.test.ts liest das Fixture nicht mehr auf Modulebene, sondern über eine Funktion je Test mit Klartext-Guard. Saboteur „Fixture umbenannt" → 5 rot, 14× Klartext, kein ENOENT, keine kollabierte Testdatei (vorher: Datei kollabiert, 16 Tests laufen nicht).
  • R48b-3 (Nachzieh, mitgenommen) — die Fehlermeldung zeigt Hashes als Anfang…Ende (418a4bea6bb2…e95f), damit ein Unterschied im letzten Zeichen sichtbar ist.
  • R48b-4/5/6 (Wächter-Reihenfolge im Bundle-Skript, Regelbetrieb-Test, „ohne Bohrungen" im Bildschirm) bleiben Nachziehposten.

60/60 in den vier Werkstoff-Dateien · Suite 446 + 24 benannte Skips · tsc 0 · eslint grün · Gate grün. CI läuft am Head 5b10aae — Meldung bei Grün, dann deine finale Messung.

@Bernhard-Reiter
Bernhard-Reiter merged commit 0d22916 into main Sep 4, 2026
2 checks passed
@Bernhard-Reiter

Copy link
Copy Markdown
Owner Author

Finale Messung (Cody #2, Head 5b10aae) — Grün, gemergt als 0d22916

Selbst gemessen im Prüfstand: check-runs 2/2 am Head · Merge-State CLEAN · R48b-1 WERKSTOFF_MODELL_PFLICHT 0 Treffer, kein toter Skip mehr · R48b-2 kein Fixture-Read auf Modulebene in szene.test.ts; Fixture per mv weg → 5 rot, kein ENOENT, keine kollabierte Testdatei · Suite CI-Form 446 passed / 24 skipped (alle 24 = die drei Altbestand-Integrationsdateien) · tsc 0 · lint 0 · Body auf den gemessenen Stand (82 grün / 0 Skips in fünf Werkstoff-Dateien) — der Squash trägt jetzt den richtigen Satz.

Nachzieh in einem Folge-PR (kein Blocker): R48b-4 Bundle-Wächter vor pnpm test ziehen · R48b-5 Regelbetrieb-Test (Seite mit unverändertem Bundle → Szene) · R48b-6 „Demo-Plan ohne Bohrungen" ins Bundle/auf den Bildschirm (getragene statt weggeschnittene Lücke).

@Bernhard-Reiter
Bernhard-Reiter deleted the werkstoff/szene branch September 4, 2026 10:21
Bernhard-Reiter added a commit that referenced this pull request Sep 4, 2026
…e auf dem Bildschirm, im Bundle gepinnt (#50)

* feat(werkstoff): die getragene Lücke auf Plan-Ebene — auftrag.hinweise auf dem Bildschirm, im Bundle gepinnt (R48b-6, R49-1/3/4/5)

„Demo-Plan ohne Bohrbild" stand nur im PR-Text von #48; Bundle und
Bildschirm sagten es nicht. Schritt 3 von 3 der Vertragsänderung
(voai#1226 Schema → cody-cad#73 `bauen --hinweis` → hier):

- Loader: `auftrag.hinweise?: string[]` strikt — Liste, 1–5 Einträge,
  je 1–200 Zeichen nach trim, getrimmt zurückgegeben; fehlt das Feld,
  fehlt es auch im Auftrag (leer ist kein Zustand).
- Seite: eine Zeile je Hinweis unter dem Plan-Hash (React escaped).
- Bundle: `auftrag.json` neu gebaut mit cody-cad#73 `bauen --hinweis
  "Demo-Plan ohne Bohrbild — 104 Bohrungen gefiltert (cody-cad#70)"`
  aus dem gefilterten Plan (65cf8e3b…) + dem ausgelieferten Modell
  (418a4bea…): dircmp gegen das alte Bundle = NUR auftrag.json anders,
  und der neue Auftrag ohne `hinweise` == der alte Auftrag. Pin in
  bundle.test.ts, damit ein späteres Bundle den Satz nicht still verliert.
- R49-1 build-demo-bundle.sh: ohne auftrag.json Abbruch (war fail-open);
  „kein Modell" wird gemeldet.
- R49-3 glbKnoten: 20 Bytes ohne Magic → „Magic", nicht „zu kurz".
- R49-4 No-op-Ternär im Stub weg. R49-5 Regelbetrieb-Pin nennt beim
  Bruch den ⚠-Satz der Seite.

Beweis: Red-First (Loader-Test, Hinweis-Zeile, ohne/kaputt je rot vor dem
Bau). Saboteure je rot: Bundle-Text gekippt (Pin + Regelbetrieb), trim
weg, max 5 weg, leer erlaubt, Feld verschluckt, Seite ohne Zeile.
Werkstoff 92/92, Suite 456 + 24 Altbestand, tsc 0, eslint 0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(werkstoff): Hinweis-Zeile — Schlüssel mit Index, Escaping-Wächter, erlaubte Seite gepinnt (Review #50)

Cody #2, vier Bedingungen: (1) key={h} kollidierte bei zwei gleichen
Hinweisen — jetzt Index+Text, Duplikate bleiben erlaubt (kein neuer
Vertragspunkt in drei Repos); (2) genau 5 × 200 als erlaubter Fall im
Loader-Test; (3) die Zusage „React escaped" hat einen Wächter: <img>,
<a>, [x](…) landen als Zeichen, 0 img / 0 a, drei Hinweise = drei Zeilen,
keine React-Klage über gleiche Schlüssel — Saboteur dangerouslySetInnerHTML
→ rot; (4) `}  let hinweise` auf zwei Zeilen. Nachzieher: 12 Bytes OHNE
Magic → „zu kurz" (Magic-zuerst-Prüfer mit ≥4-Guard bliebe sonst grün);
.hinweis-plan hat jetzt Farbe, die Zeile sieht nicht mehr aus wie der Hash.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant