Close workflow smoke template drift
This commit is contained in:
@@ -209,6 +209,93 @@ def test_choose_template_backed_output_type_prefers_requested_name():
|
|||||||
assert [template["id"] for template in matches] == ["template-1"]
|
assert [template["id"] for template in matches] == ["template-1"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_workflow_golden_template_name_maps_cases_to_expected_templates():
|
||||||
|
module = _load_render_pipeline_script()
|
||||||
|
|
||||||
|
assert module.workflow_golden_template_name({"key": "still_graph"}) == "BlenderStudio"
|
||||||
|
assert module.workflow_golden_template_name({"key": "still_shadow"}) == "BlenderStudio"
|
||||||
|
assert module.workflow_golden_template_name({"key": "turntable_graph"}) == "Blender_Studio_Schadowcatcher_Anim"
|
||||||
|
assert module.workflow_golden_template_name({"key": "unknown"}) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_workflow_smoke_template_name_maps_supported_modes_to_blenderstudio():
|
||||||
|
module = _load_render_pipeline_script()
|
||||||
|
|
||||||
|
assert module.workflow_smoke_template_name("legacy") == "BlenderStudio"
|
||||||
|
assert module.workflow_smoke_template_name("graph") == "BlenderStudio"
|
||||||
|
assert module.workflow_smoke_template_name("shadow") == "BlenderStudio"
|
||||||
|
assert module.workflow_smoke_template_name("turntable") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_ensure_output_type_render_template_binding_moves_output_type_to_preferred_template():
|
||||||
|
module = _load_render_pipeline_script()
|
||||||
|
|
||||||
|
templates_state = [
|
||||||
|
{
|
||||||
|
"id": "template-preferred",
|
||||||
|
"name": "BlenderStudio",
|
||||||
|
"is_active": True,
|
||||||
|
"output_type_ids": ["ot-existing"],
|
||||||
|
"output_type_id": "ot-existing",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "template-other",
|
||||||
|
"name": "Other Template",
|
||||||
|
"is_active": True,
|
||||||
|
"output_type_ids": ["ot-golden", "ot-other"],
|
||||||
|
"output_type_id": "ot-golden",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
patch_calls: list[tuple[str, dict]] = []
|
||||||
|
|
||||||
|
class _Response:
|
||||||
|
def __init__(self, payload: dict):
|
||||||
|
self.status_code = 200
|
||||||
|
self._payload = payload
|
||||||
|
self.text = ""
|
||||||
|
|
||||||
|
def json(self):
|
||||||
|
return self._payload
|
||||||
|
|
||||||
|
class _Client:
|
||||||
|
def patch(self, path: str, **kwargs):
|
||||||
|
payload = kwargs["json"]
|
||||||
|
patch_calls.append((path, payload))
|
||||||
|
template_id = path.rsplit("/", 1)[-1]
|
||||||
|
template = next(item for item in templates_state if item["id"] == template_id)
|
||||||
|
template["output_type_ids"] = list(payload["output_type_ids"])
|
||||||
|
template["output_type_id"] = payload["output_type_ids"][0] if payload["output_type_ids"] else None
|
||||||
|
return _Response(template)
|
||||||
|
|
||||||
|
client = _Client()
|
||||||
|
original_get_render_templates = module.get_render_templates
|
||||||
|
try:
|
||||||
|
module.get_render_templates = lambda _client: [
|
||||||
|
{
|
||||||
|
"id": item["id"],
|
||||||
|
"name": item["name"],
|
||||||
|
"is_active": item["is_active"],
|
||||||
|
"output_type_ids": list(item["output_type_ids"]),
|
||||||
|
"output_type_id": item["output_type_id"],
|
||||||
|
}
|
||||||
|
for item in templates_state
|
||||||
|
]
|
||||||
|
|
||||||
|
matches = module.ensure_output_type_render_template_binding(
|
||||||
|
client,
|
||||||
|
output_type={"id": "ot-golden", "name": "[Workflow Golden] Canonical Still Graph"},
|
||||||
|
preferred_template_name="BlenderStudio",
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
module.get_render_templates = original_get_render_templates
|
||||||
|
|
||||||
|
assert patch_calls == [
|
||||||
|
("/render-templates/template-preferred", {"output_type_ids": ["ot-existing", "ot-golden"]}),
|
||||||
|
("/render-templates/template-other", {"output_type_ids": ["ot-other"]}),
|
||||||
|
]
|
||||||
|
assert [template["name"] for template in matches] == ["BlenderStudio"]
|
||||||
|
|
||||||
|
|
||||||
def test_build_output_type_workflow_snapshot_keeps_restore_contract():
|
def test_build_output_type_workflow_snapshot_keeps_restore_contract():
|
||||||
module = _load_render_pipeline_script()
|
module = _load_render_pipeline_script()
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,299 @@
|
|||||||
|
# Next 20 Workflow Blocks
|
||||||
|
|
||||||
|
Stand: April 12, 2026
|
||||||
|
|
||||||
|
Ziel dieses Backlogs ist nicht nur weitere UI-Politur, sondern die systematische Schließung der noch offenen Paritäts-, Contract- und Authoring-Lücken zwischen Legacy- und Graph-Workflow.
|
||||||
|
|
||||||
|
## Batch A: Contract Truth And Authoring Clarity
|
||||||
|
|
||||||
|
### Block 1: 20-Block-Backlog im Repo verankern
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- sichtbare Arbeitsliste im Repo statt nur Chat-Kontext
|
||||||
|
- Batch-Reihenfolge und Quality Gates festhalten
|
||||||
|
|
||||||
|
### Block 2: Registry-/Contract-Audit gegen Runtime und Editor
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- Backend-Registry, Schema und Frontend-Contracts gegeneinander prüfen
|
||||||
|
- alle Nodes nach Kontext-, Socket-, Inspector- und Template-Input-Verhalten katalogisieren
|
||||||
|
|
||||||
|
### Block 3: Root-Context-Inputs von echten Upstream-Sockets trennen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- `cad_file_record` und `order_line_record` nicht länger als normale verkabelbare Eingänge behandeln
|
||||||
|
- Entry-Nodes im Canvas klar als kontextgesät modellieren
|
||||||
|
|
||||||
|
### Block 4: Authoring-Muster pro Node explizit machen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- Nodes klar als `Context Entry`, `Connection-Driven`, `Inspector-Driven` oder `Hybrid` kennzeichnen
|
||||||
|
- dieselbe Semantik in Canvas, Inspector und Katalog verwenden
|
||||||
|
|
||||||
|
### Block 5: Problem-Nodes mit impliziten Anforderungen katalogisieren
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- welche Nodes haben derzeit 0 Felder, aber nicht offensichtliche Laufzeitannahmen
|
||||||
|
- welche Nodes brauchen nur Dokumentation und welche echte neue Eingabevariablen
|
||||||
|
|
||||||
|
## Batch B: Input And Inspector Closure
|
||||||
|
|
||||||
|
### Block 6: Inspector-Felder gegen Runtime-Parameter mappen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- `workflow_graph_runtime.py` und Registry vollständig abgleichen
|
||||||
|
- fehlende oder irreführende Inspector-Felder schließen
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- `notify <- export_blend` Contract-Drift zwischen Frontend und Backend beseitigt
|
||||||
|
- Bridge-Node-Runtime-Parameter als explizite Contract-Sets in `workflow_graph_runtime.py` zentralisiert
|
||||||
|
- Registry-Tests prüfen jetzt auch `resolve_template`, `material_map_resolve`, `auto_populate_materials`, `glb_bbox`, `output_save` und `notify` gegen die Runtime
|
||||||
|
|
||||||
|
### Block 7: Template-Inputs und Template-Overrides konsolidieren
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- Template-Inputs als echte Produktionsvariablen sichtbar machen
|
||||||
|
- Node-Editor darf Template-spezifische Inputs nicht verstecken oder fragmentieren
|
||||||
|
|
||||||
|
Abnahme:
|
||||||
|
|
||||||
|
- `resolve_template` zeigt statische Overrides und dynamische Template-Inputs ohne Mehrdeutigkeit
|
||||||
|
- Template-Inputs werden konsistent in Inspector, Contract-Card und Preflight sichtbar
|
||||||
|
- kein Drift zwischen `workflow_input_schema`, `template_inputs` und authorbaren Node-Parametern
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- `resolve_template` zeigt jetzt statische Overrides und template-definierte Workflow-Inputs im selben Authoring-Modell statt in getrennten UI-Logiken
|
||||||
|
- aktive Templates mit `workflow_input_schema` werden auch ohne festen Override als potenzielle Produktionsvariablen sichtbar gemacht
|
||||||
|
- gezielte Frontend-Tests sichern ab, dass automatische Template-Variablenabdeckung und konkrete Override-Inputs konsistent im Inspector erscheinen
|
||||||
|
|
||||||
|
### Block 8: Render-/Output-Node-Settings auf vollständige Verträge prüfen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- Still, Turntable, Blend, Alpha-, Output- und Notification-Pfade gegen Runtime vergleichen
|
||||||
|
- fehlende Dropdowns, Modusfelder oder Guards ergänzen
|
||||||
|
|
||||||
|
Abnahme:
|
||||||
|
|
||||||
|
- alle echten Runtime-Optionen für Still/Turntable/Blend/Notify sind im Editor authorbar
|
||||||
|
- keine Fake-Felder ohne Runtime-Wirkung
|
||||||
|
- Graph/Shadow/Legacy-Handoff-Semantik ist für Output/Notify im UI klar
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- Render-Override-Felder mit Runtime-Autorität werden für `blender_still` und `blender_turntable` im Inspector gesperrt, solange `use_custom_render_settings` deaktiviert ist
|
||||||
|
- damit verschwindet eine echte Fake-Konfigurationslücke: Werte wie `samples`, `noise_threshold` oder `focal_length_mm` konnten zuvor im Graph gesetzt werden, obwohl die Runtime sie ohne aktivierte Overrides verwirft
|
||||||
|
- `output_save` und `notify` erklären ihre Handoff-Semantik jetzt explizit im Inspector, inklusive Shadow-/Graph-/Legacy-Verhalten und Failure-vs-Skip-Guards
|
||||||
|
- fokussierte Frontend- und Backend-Tests sichern die Render-/Output-/Notify-Contracts gegen erneuten Drift ab
|
||||||
|
|
||||||
|
### Block 9: Validation-Fehler inventarisieren
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- alle häufigen Validation-Fehler auf Root-Cause-Kategorien mappen
|
||||||
|
- unterscheiden zwischen Modellierungsfehler, UI-Führung, Datenfehler und Legacy-Constraint
|
||||||
|
|
||||||
|
Abnahme:
|
||||||
|
|
||||||
|
- häufige Fehlertypen sind als feste Kategorien dokumentiert
|
||||||
|
- jeder Kategorie ist eine konkrete Gegenmaßnahme zugeordnet
|
||||||
|
- UI-Validation und Backend-Preflight sprechen dieselbe Sprache
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- Inventar der real existierenden Preflight-/Validation-Codes in `VALIDATION_ERROR_INVENTORY_2026-04-12.md` dokumentiert
|
||||||
|
- feste Kategorien für `Context`, `Setup Chain`, `Data Source`, `Runtime Gap`, `Legacy Drift` und `Artifact Flow` definiert
|
||||||
|
- UI kann sich jetzt auf dieselbe Fehler-Taxonomie wie das Backend stützen
|
||||||
|
|
||||||
|
### Block 10: Preflight-Texte und Fehlermeldungen schärfen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- Fehler müssen konkret sagen, welche Verbindung oder Variable fehlt
|
||||||
|
- Legacy-vs-Graph-Abweichungen explizit benennen
|
||||||
|
|
||||||
|
Abnahme:
|
||||||
|
|
||||||
|
- Blocking-Issues benennen Node, Contract-Rolle und erwartete Abhilfe
|
||||||
|
- Preflight unterscheidet klar zwischen `error`, `warning`, `unsupported` und `legacy-drift`
|
||||||
|
- Dry-Run-Ausgabe ist für Autoren ohne Codekontext verständlich
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- `WorkflowPreflightPanel` zeigt jetzt zusätzlich einen Fehlertyp pro Issue
|
||||||
|
- Preflight-Actions und Issue-Listen verwenden konkrete Gegenmaßnahmen für reale Backend-Codes
|
||||||
|
- Legacy-Drift, Runtime-Gaps und Context-/Setup-Probleme werden im UI sichtbar getrennt
|
||||||
|
|
||||||
|
## Batch C: Authoring Surface And Node Organization
|
||||||
|
|
||||||
|
### Block 11: Node-Katalog nach Modulen und Authoring-Stufen nachschärfen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- Produktionsmodule als primäre Navigationsstruktur
|
||||||
|
- Families, Stages und Legacy/Bridge/Graph-Status sauber sichtbar machen
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- Node-Katalog zeigt jetzt zusätzlich eine explizite `Stage Coverage`-Zusammenfassung für die aktuell sichtbare Filtermenge
|
||||||
|
- Modul- und Family-Struktur bleibt erhalten, wird aber um Stage-Sichtbarkeit und Runtime-Zählung als erstes Navigationssignal ergänzt
|
||||||
|
- Raw-Node-Karten machen nun Inputs, Inspector-Variablen, Outputs und Authoring-Muster direkt sichtbar
|
||||||
|
|
||||||
|
### Block 12: Quick-Insert-/Right-Click-Menü härten
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- Canvas-Clipping beseitigen
|
||||||
|
- Tastatur- und Mausfluss robuster machen
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- Menü bleibt weiter portal-basiert und viewport-geklammert
|
||||||
|
- zusätzliche Outside-Click-Schließung und sticky Guided-Navigation härten den Canvas-Authoring-Loop
|
||||||
|
- Menü priorisiert jetzt explizit Guided Authoring vor Raw Nodes
|
||||||
|
|
||||||
|
### Block 13: Edge-Lifecycle vervollständigen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- Verbindungen löschen, neu zuweisen und Konflikte sichtbar machen
|
||||||
|
- Alternative Inputs sauber führen
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- belegte Target-Sockets werden beim Neuverbinden jetzt bewusst neu zugewiesen statt stumm geblockt
|
||||||
|
- neue Verbindungen selektieren sich direkt selbst und machen Reassignment sichtbar
|
||||||
|
- Duplicate-Verbindungen werden enger auf echte Handle-Duplikate geprüft
|
||||||
|
|
||||||
|
### Block 14: Auto-Layout enterprise-grade machen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- Überlappungen aktiv verhindern
|
||||||
|
- Knoten kollisionsfrei auseinanderdrücken
|
||||||
|
- wiederholbare, verständliche Layouts für große Graphen
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- Auto-Layout arbeitet jetzt komponentenbasiert statt alle Subgraphs in ein globales Layering zu drücken
|
||||||
|
- getrennte Subgraphs werden in stabile vertikale Bänder gesetzt
|
||||||
|
- Layout-Reihenfolge bleibt deterministisch entlang der vorhandenen Canvas-Topologie
|
||||||
|
|
||||||
|
### Block 15: Einheitliches Node-Größen- und Informationsmodell härten
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- gleiche Node-Größe, gleiche Informationshierarchie
|
||||||
|
- keine Sonderfälle pro Node-Familie
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- Node-Karten im Browser spiegeln jetzt dieselbe Informationshierarchie wie Canvas/Inspector: Inputs, Variables, Outputs und Authoring Guidance
|
||||||
|
- Nodes mit reinem Socket-Contract vs. Inspector-Variablen sind unmittelbar unterscheidbar
|
||||||
|
- kein zusätzlicher Sonderfall pro Family notwendig, weil das Contract-Summary-Modell die Darstellung trägt
|
||||||
|
|
||||||
|
## Batch D: Non-Legacy Still Workflow Completion
|
||||||
|
|
||||||
|
### Block 16: kanonischen Still-Graph vervollständigen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- saubere Referenzvariante ohne Legacy-Verkrüppelung
|
||||||
|
- alle nötigen Produktionsmodule im Editor verfügbar machen
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- kanonische Graph-Blueprints sind jetzt nicht mehr nur ein einzelner Still-Pfad, sondern als echte Referenzvarianten für Still, Alpha-Still und Still-plus-Blend vorhanden
|
||||||
|
- Referenz-Bundles im Editor decken damit die produktionsrelevanten Still-Autorenpfade sauber ab
|
||||||
|
- die Graph-Varianten bleiben parallel zum Legacy-Pfad und überschreiben ihn nicht implizit
|
||||||
|
|
||||||
|
### Block 17: Alpha-, Blend- und Varianten-Pfade komplettieren
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- alle Template- und Output-Varianten im Graph-Vertrag verfügbar machen
|
||||||
|
- Settings müssen 1:1 authorbar sein
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- Alpha-Still-Blueprint setzt transparente Defaults explizit im Graph statt über Legacy-Nebenpfade
|
||||||
|
- Blend-Delivery ist jetzt als kanonischer Referenzpfad mit `export_blend`, dediziertem `output_save` und `notify` modelliert
|
||||||
|
- Artifact-Erkennung leitet Blend- und Still-Artefakte jetzt auch über terminale `notify`- und `output_save`-Knoten korrekt her
|
||||||
|
|
||||||
|
### Block 18: Output-Type-Erstellung und Workflow-Bindings harmonisieren
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- neue Output Types müssen wieder sauber erzeugbar und bindbar sein
|
||||||
|
- keine impliziten Legacy-only Einstellungen mehr
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- graph-fähige Still-, Alpha- und Blend-Blueprints werden als bindbare Standard-Workflows mit ausgesät
|
||||||
|
- Output-Type-Bindings erkennen Blend-Support jetzt auch bei workflow-first Referenzgraphen korrekt
|
||||||
|
- gezielte API- und Frontend-Tests sichern ab, dass neue Output Types an die neuen Referenzgraphen gebunden werden können
|
||||||
|
|
||||||
|
## Batch E: Consolidation, QA And Release Gate
|
||||||
|
|
||||||
|
### Block 19: Test- und Browser-QA sequenziell ausbauen
|
||||||
|
|
||||||
|
Status: abgeschlossen
|
||||||
|
|
||||||
|
- low-RAM Testfolge statt Parallel-Last
|
||||||
|
- Editor-Flows, Validation und Autorenpfade browserseitig prüfen
|
||||||
|
|
||||||
|
Zwischenstand:
|
||||||
|
|
||||||
|
- gezielter Frontend-Regressionstest deckt jetzt den echten Admin-Fall ab, dass valide graph-gebundene Output Types nicht fälschlich als `workflow_missing` blockiert werden
|
||||||
|
- Live-Browser-QA gegen `http://localhost:5173/admin` bestätigt, dass graph- und shadow-gebundene Output Types wieder korrekte Rollout-Zustände zeigen
|
||||||
|
- die falschen Blocker `The selected workflow definition could not be resolved.` und `Linked workflow needs contract fixes before rollout.` treten für valide Bindings nicht mehr auf
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- Live-Browser-Durchlauf gegen `http://127.0.0.1:5173/workflows` bestätigt den echten Autorenpfad für den Graph-Editor: Workflow wählen, Order-Line-Kontext laden, Dry Run ausführen, Preflight lesen
|
||||||
|
- der Graph-Dry-Run des `Still Graph Blueprint` läuft mit echtem Order-Line-Kontext erfolgreich durch und macht `Run` danach korrekt freischaltbar
|
||||||
|
- der zuvor verbleibende `template_missing`-Drift im Smoke-Setup ist als echte Resource-Lücke identifiziert und geschlossen
|
||||||
|
- Smoke-Output-Types werden jetzt wie die Golden-Fälle aktiv an `BlenderStudio` gebunden, sodass der Dry-Run für die betroffene reale Order Line ohne Restwarnung durchläuft
|
||||||
|
|
||||||
|
### Block 20: Commit, Doku-Update und Restrisiken festhalten
|
||||||
|
|
||||||
|
Status: in Arbeit
|
||||||
|
|
||||||
|
- saubere Zwischenabsicherung
|
||||||
|
- verbleibende echte Produktgaps transparent dokumentieren
|
||||||
|
|
||||||
|
Zwischenstand:
|
||||||
|
|
||||||
|
- Drift-Schließung ist gezielt abgesichert: `backend/tests/domains/test_workflow_smoke_harness.py` prüft die Smoke-Template-Zuordnung explizit
|
||||||
|
- Live-Verifikation gegen `http://localhost:8888/api/workflows/484ea831-b274-4ba2-8385-7957e0ccd7f3/preflight?context_id=d6d00bd5-3c4f-4df2-98c4-eaab37df2811` liefert nach Re-Provisionierung `Graph runtime is ready for this context.`
|
||||||
|
- die Änderung sitzt bewusst im Seed-/Harness-Pfad statt in der Runtime-Validierung; es wurde keine Warnung kaschiert
|
||||||
|
|
||||||
|
Restrisiken:
|
||||||
|
|
||||||
|
- Golden- und Smoke-Template-Bindings hängen aktuell an den vorhandenen Admin-Templates; bei manueller Template-Löschung in `/admin` entsteht der Drift korrekt erneut
|
||||||
|
- die Smoke-Provisionierung deckt weiter nur Still-Fälle ab; zusätzliche Smoke-Setups für Turntable oder Blend bleiben separat zu verifizieren
|
||||||
|
- Block 20 ist erst abgeschlossen, wenn der jetzige Zwischenstand als gezielter Commit gesichert ist
|
||||||
|
|
||||||
|
## Quality Gates
|
||||||
|
|
||||||
|
- Legacy-Workflow bleibt jederzeit lauffähig
|
||||||
|
- keine zusätzliche implizite Node-Logik ohne expliziten Contract
|
||||||
|
- Root-Kontext und Upstream-Verbindungen werden im Editor nicht vermischt
|
||||||
|
- Tests laufen sequenziell und gezielt
|
||||||
|
- jede neue UI-Aussage muss mit Registry/Runtime konsistent sein
|
||||||
|
|
||||||
|
## Aktuelle Batch-Reihenfolge
|
||||||
|
|
||||||
|
1. Block 20 vorbereiten und nur reale Restrisiken statt vermeintlicher UI-Brüche festhalten
|
||||||
|
2. verbleibende Editor-/Validation-Lücken nur noch aus echten Autorenflüssen ableiten
|
||||||
|
3. Zwischenabsicherung mit Commit schließen
|
||||||
|
4. danach mit dem nächsten Implementierungsblock fortfahren
|
||||||
@@ -494,6 +494,96 @@ def choose_template_backed_output_type(
|
|||||||
raise RuntimeError("No active template-backed still-image output type was found")
|
raise RuntimeError("No active template-backed still-image output type was found")
|
||||||
|
|
||||||
|
|
||||||
|
WORKFLOW_GOLDEN_TEMPLATE_BY_KEY = {
|
||||||
|
"still_legacy": "BlenderStudio",
|
||||||
|
"still_graph": "BlenderStudio",
|
||||||
|
"still_shadow": "BlenderStudio",
|
||||||
|
"turntable_graph": "Blender_Studio_Schadowcatcher_Anim",
|
||||||
|
"blend_graph": "BlenderStudio",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def workflow_golden_template_name(case: dict) -> str | None:
|
||||||
|
return WORKFLOW_GOLDEN_TEMPLATE_BY_KEY.get(str(case.get("key") or "").strip())
|
||||||
|
|
||||||
|
|
||||||
|
def _template_output_type_ids(template: dict) -> list[str]:
|
||||||
|
linked_output_type_ids = [
|
||||||
|
str(candidate_id)
|
||||||
|
for candidate_id in (template.get("output_type_ids") or [])
|
||||||
|
if candidate_id is not None
|
||||||
|
]
|
||||||
|
fallback_output_type_id = template.get("output_type_id")
|
||||||
|
if fallback_output_type_id and str(fallback_output_type_id) not in linked_output_type_ids:
|
||||||
|
linked_output_type_ids.append(str(fallback_output_type_id))
|
||||||
|
return linked_output_type_ids
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_output_type_render_template_binding(
|
||||||
|
client: APIClient,
|
||||||
|
*,
|
||||||
|
output_type: dict,
|
||||||
|
preferred_template_name: str | None,
|
||||||
|
) -> list[dict]:
|
||||||
|
if not preferred_template_name:
|
||||||
|
return []
|
||||||
|
|
||||||
|
render_templates = get_render_templates(client)
|
||||||
|
preferred_template = find_named(render_templates, preferred_template_name)
|
||||||
|
if preferred_template is None:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Required render template '{preferred_template_name}' is missing in /admin"
|
||||||
|
)
|
||||||
|
if not preferred_template.get("is_active", True):
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Required render template '{preferred_template_name}' is inactive in /admin"
|
||||||
|
)
|
||||||
|
|
||||||
|
output_type_id = str(output_type["id"])
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
for template in render_templates:
|
||||||
|
current_ids = _template_output_type_ids(template)
|
||||||
|
desired_ids = [candidate_id for candidate_id in current_ids if candidate_id != output_type_id]
|
||||||
|
|
||||||
|
if template["id"] == preferred_template["id"] and output_type_id not in desired_ids:
|
||||||
|
desired_ids.append(output_type_id)
|
||||||
|
|
||||||
|
if desired_ids == current_ids:
|
||||||
|
continue
|
||||||
|
|
||||||
|
resp = client.patch(
|
||||||
|
f"/render-templates/{template['id']}",
|
||||||
|
json={"output_type_ids": desired_ids},
|
||||||
|
)
|
||||||
|
if resp.status_code != 200:
|
||||||
|
raise RuntimeError(
|
||||||
|
"Golden render-template link update failed "
|
||||||
|
f"({template.get('name')} → {output_type.get('name')}): "
|
||||||
|
f"{resp.status_code} {resp.text[:400]}"
|
||||||
|
)
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
if changed:
|
||||||
|
info(
|
||||||
|
"Ensured golden template binding: "
|
||||||
|
f"{output_type.get('name')} -> {preferred_template_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
refreshed_templates = get_render_templates(client)
|
||||||
|
matches = render_template_candidates_for_output_type(
|
||||||
|
refreshed_templates,
|
||||||
|
output_type_id,
|
||||||
|
active_only=False,
|
||||||
|
)
|
||||||
|
if not any(template.get("name") == preferred_template_name for template in matches):
|
||||||
|
raise RuntimeError(
|
||||||
|
"Golden render-template binding verification failed for "
|
||||||
|
f"{output_type.get('name')} -> {preferred_template_name}"
|
||||||
|
)
|
||||||
|
return matches
|
||||||
|
|
||||||
|
|
||||||
def build_output_type_workflow_snapshot(output_type: dict) -> dict:
|
def build_output_type_workflow_snapshot(output_type: dict) -> dict:
|
||||||
return {
|
return {
|
||||||
"workflow_definition_id": output_type.get("workflow_definition_id"),
|
"workflow_definition_id": output_type.get("workflow_definition_id"),
|
||||||
@@ -510,6 +600,13 @@ def smoke_workflow_name(execution_mode: str) -> str:
|
|||||||
return f"[Workflow Smoke] Canonical Still {execution_mode.title()}"
|
return f"[Workflow Smoke] Canonical Still {execution_mode.title()}"
|
||||||
|
|
||||||
|
|
||||||
|
def workflow_smoke_template_name(execution_mode: str) -> str | None:
|
||||||
|
normalized_mode = str(execution_mode or "").strip().lower()
|
||||||
|
if normalized_mode in {"legacy", "graph", "shadow"}:
|
||||||
|
return "BlenderStudio"
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def build_workflow_golden_cases() -> list[dict]:
|
def build_workflow_golden_cases() -> list[dict]:
|
||||||
still_invocation = {
|
still_invocation = {
|
||||||
"width": 1024,
|
"width": 1024,
|
||||||
@@ -688,6 +785,18 @@ def ensure_workflow_still_smoke_resources(
|
|||||||
output_type = resp.json()
|
output_type = resp.json()
|
||||||
info(f"Reusing smoke output type: {output_type_name}")
|
info(f"Reusing smoke output type: {output_type_name}")
|
||||||
|
|
||||||
|
preferred_template_name = workflow_smoke_template_name(execution_mode)
|
||||||
|
if preferred_template_name:
|
||||||
|
bound_templates = ensure_output_type_render_template_binding(
|
||||||
|
client,
|
||||||
|
output_type=output_type,
|
||||||
|
preferred_template_name=preferred_template_name,
|
||||||
|
)
|
||||||
|
info(
|
||||||
|
"Smoke template candidates: "
|
||||||
|
+ ", ".join(template.get("name") or "<unnamed>" for template in bound_templates)
|
||||||
|
)
|
||||||
|
|
||||||
workflow = None
|
workflow = None
|
||||||
if execution_mode != "legacy":
|
if execution_mode != "legacy":
|
||||||
workflows = get_workflows(client)
|
workflows = get_workflows(client)
|
||||||
@@ -790,6 +899,18 @@ def ensure_workflow_golden_resources(
|
|||||||
output_type = resp.json()
|
output_type = resp.json()
|
||||||
info(f"Reusing golden output type: {case['output_type_name']}")
|
info(f"Reusing golden output type: {case['output_type_name']}")
|
||||||
|
|
||||||
|
preferred_template_name = workflow_golden_template_name(case)
|
||||||
|
if preferred_template_name:
|
||||||
|
bound_templates = ensure_output_type_render_template_binding(
|
||||||
|
client,
|
||||||
|
output_type=output_type,
|
||||||
|
preferred_template_name=preferred_template_name,
|
||||||
|
)
|
||||||
|
info(
|
||||||
|
"Golden template candidates: "
|
||||||
|
+ ", ".join(template.get("name") or "<unnamed>" for template in bound_templates)
|
||||||
|
)
|
||||||
|
|
||||||
workflow = None
|
workflow = None
|
||||||
workflow_name = case.get("workflow_name")
|
workflow_name = case.get("workflow_name")
|
||||||
workflow_config = case.get("workflow_config")
|
workflow_config = case.get("workflow_config")
|
||||||
|
|||||||
Reference in New Issue
Block a user