[{"id":"A21","fact":"**`list_editor_commands` can populate `canExecute` for only 2 of 84 binding contexts, so `includeCanExecute` changes nothing on most payloads and that is NOT a bug.** Every row carries `mappedInLists`; when it is 0 the flag has nothing to run a predicate against and `canExecute` is null whatever the flag says. The two contexts that work on a 5.3 probe are `LevelViewport` and `ContentBrowser`, named by the endpoint's own `commandListSource.contextsWithLists`. **Reading the handler alone gives the WRONG answer here** - the flag IS consumed and does set two fields per row (`MifBridgeUI.cpp:647-658`), which is true and does not explain the measurement. **RE-MEASURED 2026-09-20 on a live 5.8.2 editor: 4 of 183, not 2 of 84 - but the MECHANISM is unchanged and the counts are not attributable to the engine alone.** `list_editor_commands` reported `contextCount` 183, `knownContextCount` 183, `matchedCommands` 3575, `emittedCommands` 400, `truncated:true`, and `commandListSource.contextsWithLists: [\"LevelViewport\", \"AdvancedRenamer\", \"ContentBrowser\", \"ActorBrowsingModeCommands\"]` - four contexts, against the two recorded on 5.3. The limitation is the same one: only a context that has actually registered a command list can answer `canExecute`. **The count difference must not be read as an engine change**: the number of binding contexts depends on which plugins and editor modules are loaded, and this measurement is a different PROJECT as well as a different engine. What is engine-independent here is the mechanism and the default truncation at 400 emitted commands, which the response states.","source":"5.3","engine":"","ue":[],"bl":[],"section":"MifBridge API contracts","value":"live, 5.3 probe port 8843, 2026-09-10, re-runnable via `tools/probe_payloads.py`: `{limit:40}` with the flag off vs on gave **0 vs 0** rows with `canExecuteKnown` and `mappedInLists` 0 on all 40; `{context:\"LevelViewport\",limit:60}` gave **0 off, 60 on**, split 54 `canExecute:true` / 6 false, and `{context:\"ContentBrowser\"}` 0 vs 6. `contextCount` and `knownContextCount` both 84. **CONFIRMED IN ENGINE SOURCE 2026-09-10** (this row first said \"NOT INDEPENDENTLY CONFIRMED - MifBridge's own claim, quoted, not read from engine source here\"): the endpoint's `commandListSource.limitation` attributes this to `FInputBindingManager` storing no command lists, with MifBridge subscribing to `OnRegisterCommandList` at PostEngineInit, after the editor UI is built. Read in both UE 5.3 and 5.3.2 source: `Runtime/Slate/Private/Framework/Commands/InputBindingManager.cpp:561-569` is `RegisterCommandList`, which only `Broadcast`s to `OnRegisterCommandList` when something is bound and stores nothing - so a command list registered before the subscription is never seen."},{"id":"A32","fact":"**`UWidgetTree::RemoveWidget` only UNLINKS - the removed widget keeps its name and stays outered to the tree, so it is still a SOURCE widget and the compiler puts it back in the GUID map.** Calling `OnVariableRemoved` alone therefore looks like it does nothing: the entry really is removed, and then `ValidateAndFixUpVariableGuids` walks `ForEachSourceWidget`, finds the widget still there, ensures \"Widget [%s] was added but did not get a GUID\" (WidgetBlueprintCompiler.cpp:794) and re-adds it with a FRESH GUID. The engine's own delete path renames each removed widget to the transient package first - WidgetBlueprintEditorUtils.cpp:638, \"so that it doesn't conflict with future widgets sharing the same name\" - and only then calls OnVariableRemoved, guarded by a check that no surviving source widget still answers to the name.","source":"UE 5.7.4","engine":"","ue":[],"bl":[],"section":"MifBridge API contracts","value":"live UE 5.7.4, 2026-09-14. Remove a VerticalBox holding a TextBlock: both remain in WidgetVariableNameToGuidMap afterwards **with different GUIDs than before** (DbgParent 9FDD5859->F1A8A8F4, DbgChild CC175C7F->822B9B98) and in a different order - removed by us, re-added by the compiler. Adding the transient rename makes the entries stay gone and the name reusable unsuffixed. Source read: RemoveWidget WidgetTree.cpp:128-155 calls RemoveChild / nulls RootWidget / clears a named slot and touches the object no further."},{"id":"A33","fact":"**`UWidgetTree::GetChildWidgets(W, Out)` does NOT include W** - despite the name, it forwards to `ForWidgetAndChildren` (WidgetTree.cpp:184), and the engine's own `ForEachWidget` calls `Predicate(RootWidget)` separately before forwarding to it (:191-199), which is only necessary because the forwarded call skips the widget passed in.","source":"UE 5.7.4","engine":"","ue":[],"bl":[],"section":"MifBridge API contracts","value":"live UE 5.7.4, 2026-09-14. A removal loop written over GetChildWidgets alone removed the CHILD from the GUID map and left the PARENT - and re-adding a widget under the parent's name then tripped OnVariableAdded's own `ensure(!WidgetVariableNameToGuidMap.Contains(VariableName))` (WidgetBlueprint.cpp:1025). MifBridgeWidgets' comment had claimed for months that it includes the widget itself; the response code happened to be right because it added the named widget by hand."},{"id":"M530b1b","fact":"The widget branch of set_property runs a FULL COMPILE on every write and refuses itself inside batch (reinstancing captured by batch's open transaction leaves a dead CDO for the next Ctrl-Z); the objectPath branch - CDO edits, component templates, node properties, placed actors - compiles nothing and is freely batchable.","source":"docs/02_GOTCHAS.md §5d § Caveat and §7; IsCompileHeavyEndpoint at MifBridgeCommon.cpp:1337, the derivation-minus-set_property at :1350 (verified verbatim); batch's refusal branch at MifBridgeNodes.cpp:2770. self_audit reports the live set as transactionBuckets.compileHeavy - read that, not a document list.","engine":"","ue":[],"bl":[],"section":"MifBridge batching","value":"`return (IsSelfManagedEndpoint(Endpoint) && Endpoint != TEXT(\"set_property\")) || ...`"},{"id":"Mba6317","fact":"In the bridge's type grammar 'float', 'float32' and 'single' produce a true 32-bit PC_Real+PC_Float pin, while 'double', 'float64' and 'real' produce 64-bit PC_Real+PC_Double. This changed: previously all six produced a 64-bit pin, which made UMG TAttribute<float> delegate bindings (PercentDelegate, OpacityDelegate) unreachable because a double-returning UFUNCTION fails the delegate signature match.","source":"MifBridgeCommon.cpp MakePinType at :4587, the two branches at :4656-4672 (verified verbatim, including the UMG reasoning in the source comment); docs/02_GOTCHAS.md §2","engine":"","ue":[],"bl":[],"section":"MifBridge type grammar","value":"`float|float32|single -> PC_Real + PC_Float (32-bit); double|float64|real -> PC_Real + PC_Double (64-bit)`"},{"id":"M127530","fact":"USubobjectDataSubsystem::ReparentSubobjects is UI-locked in Blueprint contexts — it hard-requires an SCS-editor preview actor a headless bridge does not have.","source":"Engine/Source/Editor/SubobjectDataInterface/Private/SubobjectDataSubsystem.cpp:1817-1823 (function opens at :1803). CORRECTION: the candidate cited :1818-1824, off by one","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE blueprint authoring","value":"`if (Params.BlueprintContext) { if (!Params.ActorPreviewContext) { UE_LOG(... \"Failed to reparent: In a blueprint context there must be an actor preview!\"); return false; } }`"},{"id":"M380e7f","fact":"FBlueprintEditorUtils' only graph-move API is intra-Blueprint ORDERING: MoveGraphBeforeOtherGraph reorders a function/macro graph within its existing parent. It is not a cross-Blueprint move.","source":"Engine/Source/Editor/UnrealEd/Public/Kismet2/BlueprintEditorUtils.h:1215 (exact; FindIndexOfGraphInParent at :1212). NOTE: the candidate's universal negative (\"No engine API moves a function or graph between Blueprints\") is NOT establishable from this one declaration and has been dropped","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE blueprint authoring","value":"`static UNREALED_API bool MoveGraphBeforeOtherGraph(UEdGraph* Graph, int32 NewIndex, bool bDontRecompile);`"},{"id":"M0d3308","fact":"FKismetBytecodeDisassembler is NOT in Editor/KismetCompiler — it lives in Developer/ScriptDisassembler with method-level SCRIPTDISASSEMBLER_API, so using it is a new Build.cs module dependency; that module itself depends only on Core and CoreUObject.","source":"Engine/Source/Developer/ScriptDisassembler/Public/ScriptDisassembler.h:37 (ctor), :44 (DisassembleStructure), :52 (DisassembleAllFunctionsInClasses) — all method-level SCRIPTDISASSEMBLER_API; dependency list read verbatim from Developer/ScriptDisassembler/ScriptDisassembler.Build.cs","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE blueprint authoring","value":"`PublicDependencyModuleNames.AddRange(new string[] { \"Core\", \"CoreUObject\" });`"},{"id":"M6becaf","fact":"FCompilerResultsLog carries node and pin back-references, so every compile message can be mapped to the offending NodeGuid and pin name and returned as structured JSON - this is the feature that replaces reading errors off a screenshot.","source":"docs/05_DESIGN_SPEC.md §8 (:311-328) proposed it; VERIFIED AS BUILT rather than left as a plan - MifBridgeIntrospect.cpp:2722 declares the FCompilerResultsLog, :2733 iterates Results.Messages, :2751 emits pinName, and the endpoint's own summary at :2775 promises messages[{severity,text,nodeGuid,pinName}]","engine":"","ue":[],"bl":[],"section":"UE Blueprint compile","value":"`FCompilerResultsLog Results; FKismetEditorUtilities::CompileBlueprint(Blueprint, EBlueprintCompileOptions::None, &Results);`"},{"id":"M8f3a02","fact":"PlaceAndInit must only call AllocateDefaultPins() when Node->Pins.Num() == 0: UK2Node_FunctionResult::PostPlacedNewNode() calls SyncWithEntryNode() which reconstructs and allocates pins, and UK2Node_FunctionResult::AllocateDefaultPins creates the exec pin with no FindPin guard - so the standard spawn sequence minted a permanent duplicate exec pin.","source":"docs/01_POSTMORTEMS.md PM-004 (:1493); engine 5.3.2 source  K2Node_FunctionResult.cpp verified verbatim - AllocateDefaultPins at :165-167 opens with an unguarded `CreatePin(EGPD_Input, PC_Exec, PN_Execute)`, and PostPlacedNewNode at :257-262 calls SyncWithEntryNode(). Contrast noted in the PM: UK2Node_EditablePinBase::Alloc","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE Blueprint nodes","value":"`Graph->AddNode(...); Node->CreateNewGuid(); Node->PostPlacedNewNode(); if (Node->Pins.Num()==0) Node->AllocateDefaultPins();`"},{"id":"M1e3edb","fact":"add_function_call selects the same UK2Node_CallFunction subclass the engine would and reports it as nodeClass; UK2Node_PromotableOperator is deliberately never selected because the engine gates it on type-promotion registry state and one spawned outside that path comes up with unresolved wildcards.","source":"docs/02_GOTCHAS.md §4c (lines 484-508). The same section supersedes the old 'Array_Find won't stay typed' gotcha: the cause was the spawned node class, and UK2Node_CallArrayFunction owns the wildcard-propagation logic a plain CallFunction lacks.","engine":"","ue":[],"bl":[],"section":"UE Blueprint nodes","value":"`MD_ArrayParam -> UK2Node_CallArrayFunction; MD_DataTablePin -> UK2Node_CallDataTableFunction; commutative+pure -> UK2Node_CommutativeAssociativeBinaryOperator; MD_MaterialParameterCollectionFunction -> UK2Node_CallMaterialParameterCollectionFunction; interface`"},{"id":"M960723","fact":"A function's inputs live on the ENTRY node as EGPD_Output and its outputs live on the Return node as EGPD_Input - add_pin takes direction in function terms and maps it, but addressing those nodes directly shows the directions inverted. Outputs are mirrored onto every sibling Return node with the same name, because a graph with several Returns shares one signature.","source":"docs/02_GOTCHAS.md §5 § add_pin (lines 511-532). Related consequences recorded there: a custom event has no outputs (direction:'output' is refused), and adding an output to a void function creates a Return node and wires it from the entry's exec or the out-param is dead-code-eliminated while compiling clean.","engine":"","ue":[],"bl":[],"section":"UE Blueprint nodes"},{"id":"M4bea13","fact":"Pin connection must go through the K2 schema so wildcard resolution fires and the refusal reason is human-readable: CanCreateConnection returns a reason string on failure, TryCreateConnection fires NotifyPinConnectionListChanged which is what resolves wildcards.","source":"docs/05_DESIGN_SPEC.md §7.10 (:296-304) proposed it; VERIFIED AS BUILT - Schema->CanCreateConnection call sites in MifBridgeCommon.cpp at :3663, :3672, :3759, :3768 and :5122. Caveat from docs/02_GOTCHAS.md §10: UEdGraphSchema_K2::ArePinTypesCompatible returns true unconditionally when either side is PC_Wildcard, so a","engine":"","ue":[],"bl":[],"section":"UE Blueprint pins","value":"`const UEdGraphSchema_K2* K2 = GetDefault<UEdGraphSchema_K2>(); const FPinConnectionResponse R = K2->CanCreateConnection(Out, In); if (R.Response == CONNECT_RESPONSE_DISALLOW) return Error(R.Message); K2->TryCreateConnection(Out, In);`"},{"id":"M0642de","fact":"Making a custom event an RPC RENAMES it - a custom event MulticastBoom came back as Multicast_MulticastBoom after replicates:'multicast', because the engine prefixes the function name to match the RPC convention. The node GUID is stable; address the event by nodeGuid after changing replication and re-read the returned target for the new name, because anything holding the old name (find_nodes byTitle, set_function_flags by name, a call site addressed by name) goes stale.","source":"docs/02_GOTCHAS.md §4b, the 'Making an event an RPC RENAMES it' blockquote (:477-484), marked 'Verified live'","engine":"","ue":[],"bl":[],"section":"UE Blueprint replication","value":"`replicates: none | multicast | server | client ; multicast -> Multicast_<Name>`"},{"id":"M01e036","fact":"set_variable_flags is a partial update on member variables only (asking for these on a local is an explicit error, not a silent no-op); repNotify implies replicated and CREATES the OnRep_<Var> function graph if absent because the compiler errors without it, reported as createdRepNotifyGraph; exposeOnSpawn implies instanceEditable; and a replicated variable does nothing unless the owning Actor has bReplicates, which the bridge warns about via replicationWarning but never flips.","source":"docs/02_GOTCHAS.md §4 (:420-446)","engine":"","ue":[],"bl":[],"section":"UE Blueprint variables","value":"`replicationCondition takes an ELifetimeCondition (COND_None, COND_OwnerOnly, COND_SkipOwner, COND_InitialOnly, ...), COND_ prefix optional`"},{"id":"M87fc1c","fact":"replicationWarning's exact condition, read from source rather than a doc (row above cites only 02_GOTCHAS): it is emitted only when the variable carries CPF_Net AND the blueprint's GeneratedClass default object casts to AActor AND that CDO's GetIsReplicated() is false. A non-Actor blueprint falls out of the Cast and never gets it; the bridge never sets bReplicates itself; and the text names the fix (set_property bReplicates on the class default object). A fresh Actor blueprint has bReplicates=false, so one replicated variable is enough to provoke it.","source":"Source/MifBridge/Private/MifBridgeIntrospect.cpp:1325-1337, read verbatim 2026-09-10. Which endpoints reach this block (set_variable_flags for certain; add_variable per the spec's ApplyVariableFlags note) was NOT traced here and is not claimed","engine":"","ue":[],"bl":[],"section":"U218","value":"`if ((Var->PropertyFlags & CPF_Net) != 0) { if (AActor* ActorCDO = Blueprint->GeneratedClass ? Cast<AActor>(Blueprint->GeneratedClass->GetDefaultObject()) : nullptr) { if (!ActorCDO->GetIsReplicated()) { Out->SetStringField(\"replicationWarning\", ...); } } }`"},{"id":"M2b19fd","fact":"UEditorUtilitySubsystem::TryRun on cooked Editor Utility Blueprints is a dead end — the class it runs is resolved either from Asset->GetClass() or, only for a UBlueprint, from Blueprint->GeneratedClass; pak-mounted content ships only the generated class and no UBlueprint, so any run_editor_utility endpoint is loose-assets-only.","source":"Engine/Source/Editor/Blutility/Private/EditorUtilitySubsystem.cpp:141-145, inside UEditorUtilitySubsystem::TryRun (opens :133)","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE editor utility blueprints","value":"`UClass* ObjectClass = Asset->GetClass(); if (UBlueprint* Blueprint = Cast<UBlueprint>(Asset)) { ObjectClass = Blueprint->GeneratedClass; }`"},{"id":"M9c06fe","fact":"UIKRetargeterController::SetIKRig is not an assignment - it also copies the preview mesh off the rig, rebuilds the chain mapping and runs AutoMapChains(Fuzzy); AutoMapChains with bForceRemap=false skips every chain that already has a source (making a Clear call a guaranteed no-op); and UIKRetargeter::ChainMapping has been DEPRECATED since 5.1, with the live mapping in ChainSettings.","source":"Engine 5.3.2 source  IKRetargeterController.cpp - SetIKRig at :52-82 (verified: assigns SourceIKRigAsset AND SourcePreviewMesh), the AutoMapChains skip at :334-340 (doc says :336-340, verified verbatim); IKRetargeter.h:18 verified as `struct UE_DEPRECATED(5.1, \"Use URetargetChainSettings instead.\") FRetargetChainMap;`. docs","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE IK Retargeter","value":"`const bool bIsMappedAlready = ChainMap->SourceChain != NAME_None; if (bIsMappedAlready && !bForceRemap) { continue; }`"},{"id":"M6f5d52","fact":"Three UIKRigController calls succeed while doing something else: SetRetargetRoot given an absent bone sets NAME_None and returns true; AddRetargetChain silently renames on collision via GetUniqueRetargetChainName and RETURNS the name it used; and AddRetargetChain only verifies both bones exist, so a chain whose end bone is not a descendant of its start is stored and spans nothing.","source":"Engine 5.3.2 source  Plugins/Animation/IKRig/Source/IKRigEditor/Private/RigEditor/IKRigController.cpp - SetRetargetRoot's clear-and-return-true at :389-403 (doc says :391-403), the existence-only bone checks at :183-193 (verified verbatim, both return NAME_None only for a MISSING bone, never a hierarchy check), the GetUniqu","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE IK Rig","value":"`if (RootBoneName != NAME_None && Asset->Skeleton.GetBoneIndexFromName(RootBoneName) == INDEX_NONE) { NewRootBone = NAME_None; } ... return true;`"},{"id":"M073618","fact":"USkeletalMesh::WaitUntilAsyncPropertyReleased IS exported (ENGINE_API), so the inline GetMorphTargets() accessor links fine from external modules — the earlier \"link trap\" claim was wrong. The real caveat is behavioural: the accessor synchronously WAITS if the mesh is mid-async-compilation.","source":"Engine/Source/Runtime/Engine/Classes/Engine/SkeletalMesh.h:2708 (exported helper) and :1794-1800 (GetMorphTargets calling it at :1796); base helper Runtime/Engine/Classes/Engine/SkinnedAsset.h:301","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE skeletal mesh","value":"`ENGINE_API void WaitUntilAsyncPropertyReleased(ESkeletalMeshAsyncProperties AsyncProperties, ESkinnedAssetAsyncPropertyLockType LockType = ESkinnedAssetAsyncPropertyLockType::ReadWrite) const;`"},{"id":"M445684","fact":"UWidgetAnimation::BindPossessableObject asserts without a live preview widget because it CastChecked's its Context to UUserWidget — write the FWidgetAnimationBinding UPROPERTYs directly instead.","source":"Engine/Source/Runtime/UMG/Private/Animation/WidgetAnimation.cpp:156 (function opens :154). CORRECTION: the candidate cited :157, off by one. Binding struct at Runtime/UMG/Public/Animation/WidgetAnimationBinding.h:17-31 (WidgetName/SlotWidgetName/AnimationGuid/bIsRootWidget, all UPROPERTY)","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE UMG","value":"`UUserWidget* PreviewWidget = CastChecked<UUserWidget>(Context);`"},{"id":"M06b72e","fact":"UWidgetAnimation times are FFrameNumber in TICK space, not seconds and not display frames - a UMovieScene's display rate is typically 20fps for UMG while its tick resolution is typically 60000/1. The playback range end is exclusive: a 1.5s animation ends at tick 90001.","source":"docs/02_GOTCHAS.md §6b. ENGINE CITATION CORRECTED: the +1 is at AnimationTabSummoner.cpp:611 on 5.3 (verified verbatim), not :589 as the doc says - :589 is the NewObject<UWidgetAnimation> line.","engine":"","ue":[],"bl":[],"section":"UE UMG animation","value":"`0.95 seconds -> 57000 ticks (0.95 * 60000); NewAnimation->MovieScene->SetPlaybackRange(TRange<FFrameNumber>(InFrame.FrameNumber, OutFrame.FrameNumber+1));`"},{"id":"M122777","fact":"UWidgetAnimation::BindPossessableObject opens with CastChecked<UUserWidget>(Context), which is a check() - a null or wrong-typed context TERMINATES the process rather than returning null. Its plain-widget branch is four assignments that never read Context, which is what MifBridge replicates; the root-widget case is refused rather than approximated.","source":"Engine 5.3.2 source  Runtime/UMG/Private/Animation/WidgetAnimation.cpp:157 (CastChecked, verified verbatim as the first statement of BindPossessableObject at :155) and :189-199 (the `else if (PossessedSlot == nullptr)` branch: AnimationGuid, WidgetName, bIsRootWidget, AnimationBindings.Add - Context unread). docs/02_GOTCHAS","engine":"5.3.2","ue":["5.3"],"bl":[],"section":"UE UMG animation","value":"`UUserWidget* PreviewWidget = CastChecked<UUserWidget>(Context);`"},{"id":"Me3e32a","fact":"A UMG widget binding is TWO things - an FGuid possessable in the UMovieScene AND an entry in UWidgetAnimation::AnimationBindings mapping that guid to the widget name; create or remove only one half and the animation exists, compiles, plays and moves nothing. Separately, UMovieScene::GetTracks() returns only ROOT tracks, so counting it reports trackCount:0 for a working keyed track bound to a widget.","source":"docs/02_GOTCHAS.md §6b items 2 and 'Bonus trap' (lines 777-798)","engine":"","ue":[],"bl":[],"section":"UE UMG animation","value":"`bound tracks live on FMovieSceneBinding::GetTracks()`"},{"id":"M166eda","fact":"A URichTextBlock with AutoWrapText true and WrapTextAt 0 reports its full UNWRAPPED width and a one-line height, causing both the right-edge clipping and the row-overlap bugs at once; WrapTextAt is CPF_BlueprintReadOnly so no Blueprint node can set it, but reflection (Lua) can.","source":"Eddie_v2/docs/25_EDDIEWIKI_INGAME_GUI.md:111-121 (and the sync-table row at :31)","engine":"","ue":[],"bl":[],"section":"UMG","value":"`WrapTextAt = the cell's own share of the row width`"},{"id":"U306","fact":"**Implementing an interface gives the Blueprint a function graph only for the interface functions that cannot be events - the ones with outputs; a function with no outputs is implemented as an event, with `add_override_event`.** `FBlueprintEditorUtils::ImplementNewInterface` makes a graph when `CanKismetOverrideFunction && !FunctionCanBePlacedAsEvent`. Measured on 5.8.2: `implement_interface_function` on a no-output interface function refused it as event-style and named `add_override_event`; on a function with an output, right after `add_interface`, it refused with \"function graph 'GetPrompt' already exists\" - the graph was already there.","source":"5.3 `BlueprintEditorUtils.cpp:6291`, 5.8 `BlueprintEditorUtils.cpp:6551`; measured on the 5.8.2 probe","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"U307","fact":"**A TextBlock is not a Blueprint variable when it is created: `UWidget`'s constructor sets `bIsVariable = true` and `UTextBlock`'s clears it, as CanvasPanel, Border, the boxes, SizeBox, Spacer and other layout widgets do - so a widget Blueprint's compiled class has no member for a new text block until the flag is set.** `set_widget_is_variable` sets it and regenerates the skeleton, as the designer's Is Variable box does. Measured on 5.8.2: `add_tree_widget` TextBlock under a new widget's `CanvasPanel_0` root came back isVariable false and the compiled class had no member; with the flag set it had one, and the interaction recipe reads its label through it in PIE.","source":"5.3 `TextBlock.cpp:23`, 5.8 `TextBlock.cpp:24`, 5.3 `Widget.cpp:188`, 5.8 `Widget.cpp:195`, 5.3 `CanvasPanel.cpp:17`, 5.8 `CanvasPanel.cpp:17`; measured tools/test_gameplay_recipes_live.py G213","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"U308","fact":"**An Actor's BeginPlay event is titled \"Event BeginPlay\" and an ActorComponent's \"Event Begin Play\": the two `ReceiveBeginPlay` declarations carry different DisplayName metadata, so a node found by its title has to use its owner's spelling.** Measured with `list_nodes` on 5.8.2: a new Actor Blueprint's ghost event reads \"Event BeginPlay\", a new ActorComponent Blueprint's \"Event Begin Play\".","source":"5.3 `Actor.h:1908`, 5.8 `Actor.h:2121`, 5.3 `ActorComponent.h:748`, 5.8 `ActorComponent.h:942`; measured on the 5.8.2 probe","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"U309","fact":"**A call node whose function marks a class pin `DeterminesOutputType` retypes its result the moment that pin's default changes: `UK2Node_CallFunction::PinDefaultValueChanged` conforms the output type, so Create Save Game Object with its class set already returns that class and needs no cast.** A cast after it compiles, and the compiler adds a NOTE, not a warning: \"'ReturnValue' is already a '...', you don't need Cast To ...\". Measured on 5.8.2: set_pin_default on SaveGameClass turned ReturnValue from SaveGame into the SaveGame Blueprint's class, and a compile with the cast in place counted 0 warnings and listed that note.","source":"5.3 `K2Node_CallFunction.cpp:1375`, 5.8 `K2Node_CallFunction.cpp:1374`, 5.3 `K2Node_DynamicCast.cpp:526`, 5.8 `K2Node_DynamicCast.cpp:532`; measured on the 5.8.2 probe","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"U310","fact":"**A user-defined struct's member is named `<FriendlyName>_<N>_<32-hex GUID>`, and a Make or Break node's member pins carry that name while its whole-struct pin carries the struct's name - so a member pin is found by its friendly prefix, never spelled.** Measured on 5.8.2: `create_struct` with a member Transform made `Transform_2_C8ECFA31454E5F14D5B63E88ED09DD1C`, and the struct's Make node had pins `S_State` and that name.","source":"5.3 `StructureEditorUtils.cpp:250`, 5.3 `StructureEditorUtils.cpp:255`, 5.8 `StructureEditorUtils.cpp:257`, 5.8 `StructureEditorUtils.cpp:262`; measured on the 5.8.2 probe","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"U311","fact":"**A native class's property set on a Blueprint's class default object reaches new instances only after that Blueprint compiles.** A Blueprint class copies inherited native properties onto a new instance from a list it builds when it compiles - the ones whose default differs from the native parent's - so an edit after the last compile reads back on the class default object while every new instance keeps the native default. Measured on 5.8.2: a GameModeBase Blueprint's PlayerControllerClass set on its class default object after its compile read back as the Blueprint controller, yet PIE's GameMode held `/Script/Engine.PlayerController` and spawned that; one more compile put the Blueprint controller in PIE.","source":"5.3 `BlueprintGeneratedClass.cpp:1003`, 5.3 `BlueprintGeneratedClass.cpp:1065`, 5.8 `BlueprintGeneratedClass.cpp:1209`, 5.8 `BlueprintGeneratedClass.cpp:1281`; measured on the 5.8.2 probe","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"U312","fact":"**Every UserWidget is made transactional and outered to its game instance, and the editor asserts at the end of PIE when anything still references a PIE object - so a widget made inside an open editor transaction during PIE takes the editor down at stop_pie.** The assertion names the object and its path from root, which here ran through `UTransBuffer::AddReferencedObjects`. Measured on 5.8.2: a Blueprint function that pushed a widget, called through `call_function` inside the bridge's undo transaction, crashed the editor at `stop_pie` (the bridge now runs such a call outside the transaction, A90).","source":"5.3 `UserWidget.cpp:2212`, 5.8 `UserWidget.cpp:2809`, 5.3 `PlayLevel.cpp:517`, 5.8 `PlayLevel.cpp:553`; measured on the 5.8.2 probe","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"U313","fact":"**A UserWidget is SelfHitTestInvisible by default, and AddToViewport shows it that way: its children take clicks and its root does not.** A widget set to Visible makes its whole root a click target, which under a Game and UI input mode takes the clicks the game would have had. Measured on 5.8.2: `GetVisibility` on a freshly pushed widget answered SelfHitTestInvisible.","source":"5.3 `UserWidget.cpp:85`, 5.8 `UserWidget.cpp:100`; measured on the 5.8.2 probe","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"U314","fact":"**A user enum's display name may hold spaces and punctuation: the engine judges a display name only as empty or a duplicate of another entry's, while `IsProperNameForUserDefinedEnumerator` tests an AUTHORED name against `INVALID_OBJECTNAME_CHARACTERS`, space and comma included.** Using the second on a display name refuses \"Game and UI\".","source":"5.3 `EnumEditorUtils.cpp:537`, 5.8 `EnumEditorUtils.cpp:540`, 5.3 `EnumEditorUtils.cpp:229`, 5.8 `EnumEditorUtils.cpp:232`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"A89","fact":"**`apply_graph_patch` checks every wire against the graph as it stood BEFORE the patch, so a wire that is legal only after an earlier `set_pin_default` in the same patch - a `DeterminesOutputType` class pin retyping a result (U309) - fails preflight, and nothing in the patch is applied.** Set such a default with its own `set_pin_default` call first; the patch then sees the retyped pin.","source":"measured on 5.8.2 building gameplay_recipe's save recipe: \"1 of 14 operations failed preflight - NOTHING WAS APPLIED\", and the same wires applied once the class was set by its own call","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"A90","fact":"**A bridge call whose payload names a live PIE object runs without the bridge's undo transaction - so does a `batch` holding one, and `set_property` skips its own for a PIE object - so nothing done to a PIE world reaches the undo history.**","source":"measured: tools/test_gameplay_recipes_live.py G249 on 5.8.2 (pushing, popping and clearing screens in PIE left the undo queue unchanged, and the editor answered after stop_pie; before the change the same calls crashed it, U312)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"A91","fact":"**`create_enum` and `add_enum_value` take display names with spaces, commas and periods, and still refuse a duplicate.**","source":"measured: tools/test_enums.py T306 on 5.8.2 (before the change \"Game and UI\" came back as NewEnumerator1 with a warning)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE Blueprint authoring - interface graphs, widget variables and event names"},{"id":"U357","fact":"**The widget compiler checks WidgetVariableNameToGuidMap and repairs it in the same pass. An EMPTY map is filled from every source widget and animation without an ensure; otherwise a widget with no entry ensures \"Widget [%s] was added but did not get a GUID\" and is given one, and an entry with no widget ensures \"Variable [%s] was deleted but still has a GUID\" and is removed - ensureAlways, so every time, not once.**","source":"5.8 `Editor/UMGEditor/Private/WidgetBlueprintCompiler.cpp:750-816` (ValidateAndFixUpVariableGuids); measured: tools/test_widget_guid_map.py T4609-T4612 on the unfixed 5.8.2 build logged both messages, and the map read after duplicate_tree_widget held every clone","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE widget Blueprints - the variable GUID map, and what the widget compiler repairs"},{"id":"U358","fact":"**The widget compiler's source widgets are every UWidget outered to the widget tree, not the hierarchy: a widget dropped from the hierarchy stays a source widget until garbage collection deletes it.**","source":"5.8 `Editor/UnrealEd/Private/BaseWidgetBlueprint.cpp:86-102` (ForEachSourceWidgetImpl, ForEachObjectWithOuter); measured: after move_tree_widget replaceRoot on the unfixed 5.8.2 build, the compile that followed KismetSystemLibrary::CollectGarbage named the old root and 6 more as deleted","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE widget Blueprints - the variable GUID map, and what the widget compiler repairs"},{"id":"U359","fact":"**UWidgetBlueprint::OnVariableAdded and OnVariableRemoved call Modify() on the widget Blueprint themselves, and OnVariableAdded ensures when the name is already in the map. The engine's paste registers every pasted widget, and its wrap registers the panel it makes.**","source":"5.8 `Editor/UMGEditor/Private/WidgetBlueprint.cpp:1025-1058`; `WidgetBlueprintEditorUtils.cpp:1367` (paste) and `:1483` (wrap)","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE widget Blueprints - the variable GUID map, and what the widget compiler repairs"},{"id":"A102","fact":"**`duplicate_tree_widget`, `wrap_tree_widget` and `move_tree_widget` replaceRoot keep the widget GUID map in step: every clone and wrapper is registered and every widget a root replacement drops is retired, so none of their compiles logs a GUID ensure, before or after a garbage collection. `displacedWidgets` names the dropped widgets, old root first, and `displacedSubtreeSize` counts them without the promoted subtree.**","source":"measured: tools/test_widget_guid_map.py T4609-T4612, 19/19 on 5.8.2 and 5.6.1; 7 checks failing on the unfixed build","engine":"5.6, 5.8","ue":["5.6","5.8"],"bl":[],"section":"UE widget Blueprints - the variable GUID map, and what the widget compiler repairs"},{"id":"A103","fact":"**After `remove_tree_widget`, the removed widget's name can be added again unsuffixed as a different class, with the editor still answering - on 5.3.2, where there is no GUID map and the removed widget no longer resolves under the tree, as on 5.6 and 5.8.**","source":"measured: tools/test_widget_guid_map.py T4605c, 3/3 on 5.3.2 and within 19/19 on 5.8.2 and 5.6.1; by hand first on 5.3.2, 2026-09-23","engine":"5.3, 5.6, 5.8","ue":["5.3","5.6","5.8"],"bl":[],"section":"UE widget Blueprints - the variable GUID map, and what the widget compiler repairs"},{"id":"U415","fact":"**The StateTree compile requires every Input-category property of a node's instance data to be bound unless it carries `Optional` meta - on 5.3 only the instance type's OWN properties are walked, from 5.4 its parents' too.** 5.3 walks `TFieldIterator<FProperty>(TargetStruct.Struct, EFieldIterationFlags::None)` and tests `HasMetaData(TEXT(\"Optional\"))`; 5.4-5.8 walk with the default flags (super structs included) and call `UE::StateTree::PropertyHelpers::HasOptionalMetadata`, whose body is that same HasMetaData test. The error: \"Input property '%s' on %s is expected to have a binding.\" describe_state_tree's requiredInputs (edit_state_tree, docs/27 section 11.4) is this walk.","source":"5.3 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:1658`, 5.3 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:1666`, 5.4 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:1623`, 5.4 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:1628`, 5.4 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreePropertyHelpers.cpp:148-151`, 5.5 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:2083`, 5.5 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:2118`, 5.6 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:2490`, 5.6 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:2525`, 5.7 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:2767`, 5.7 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:2802`, 5.8 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:3089`, 5.8 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:3124`, 5.8 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreePropertyHelpers.cpp:302-305`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE StateTree property bindings - what the compile requires and what the binding widget offers"},{"id":"U416","fact":"**What the StateTree binding widget offers as a binding TARGET (5.3): never a property with CPF_PersistentInstance, CPF_EditorOnly or CPF_Config; an Input or Context property only whole (a one-segment path); a Parameter at any depth; nothing else - so never an Output.** A property's usage is its `Category` meta: Input or Inputs, Output or Outputs, Context, and anything else (no category included) Parameter, compared with FString's case-insensitive ==. On 5.3 the usage function is in `UE::StateTree::Compiler` with no export macro, so a plugin restates it. edit_state_tree's addBindings applies this rule on every engine (docs/27 section 11.2).","source":"5.3 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/Customizations/StateTreeBindingExtension.cpp:478-501`, FStateTreeBindingExtension::IsPropertyExtendable; 5.3 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:180-213`, GetUsageFromMetaData","engine":"5.3","ue":["5.3"],"bl":[],"section":"UE StateTree property bindings - what the compile requires and what the binding widget offers"},{"id":"U417","fact":"**Which SOURCE the StateTree binding widget lets a target take (5.3).** An `FStateTreeAnyEnum` target takes an enum property - an FByteProperty with an enum, or an FEnumProperty - any enum when the target carries `AllowAnyBinding` meta, else only the enum the AnyEnum already holds (a null one then takes none). An `FStateTreeStructRef` with `BaseStruct` meta takes a struct whose type IsChildOf it. Anything else takes what `FStateTreePropertyBindings::GetPropertyCompatibility` does not call Incompatible, type promotion included. Only CPF_Edit properties are listed as sources. A WHOLE context struct binds only to a struct property of exactly its type or an object property whose class it IsChildOf. The compile then points an AnyEnum end of a binding at the AnyEnum's `Value` member. The Compare Enum condition's Left is such an input (Input, AllowAnyBinding). edit_state_tree's addBindings tests the same.","source":"5.3 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/Customizations/StateTreeBindingExtension.cpp:594-657`, 5.3 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/Customizations/StateTreeBindingExtension.cpp:659-686`, 5.3 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/Customizations/StateTreeBindingExtension.cpp:688-692`, 5.3 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:1401-1420`, FStateTreeCompiler::IsPropertyAnyEnum, 5.3 `Plugins/Runtime/StateTree/Source/StateTreeEditorModule/Private/StateTreeCompiler.cpp:1614-1631`, 5.3 `Plugins/Runtime/StateTree/Source/StateTreeModule/Public/Conditions/StateTreeCommonConditions.h:137-138`","engine":"5.3","ue":["5.3"],"bl":[],"section":"UE StateTree property bindings - what the compile requires and what the binding widget offers"},{"id":"U418","fact":"**A StateTree binding path's array index is checked against the value, and its last indirection is the element.** Resolving `Name[i]` with a value fails when i is past the array's end (\"Index %d out of range\"), and the indirection it ends on is the array's Inner property at the element's address - so a type check on that indirection compares the element's type. Without a value (a context object) the index is not checked.","source":"5.3 `Plugins/Runtime/StateTree/Source/StateTreeModule/Private/StateTreePropertyBindings.cpp:1447-1503`","engine":"5.3","ue":["5.3"],"bl":[],"section":"UE StateTree property bindings - what the compile requires and what the binding widget offers"},{"id":"U419","fact":"**The StateTree component schema's one context entry is named \"Actor\", of class AActor (the schema's ContextActorClass when one is set), with the fixed id 1D971B00-28884FDE-B5436802-36984FD5** - what `context.Actor` names in edit_state_tree's addBindings.","source":"5.3 `Plugins/Runtime/GameplayStateTree/Source/GameplayStateTreeModule/Private/Components/StateTreeComponentSchema.cpp:12`, 5.8 `Plugins/Runtime/GameplayStateTree/Source/GameplayStateTreeModule/Private/Components/StateTreeComponentSchema.cpp:22`, 5.8 `Plugins/Runtime/GameplayStateTree/Source/GameplayStateTreeModule/Private/Components/StateTreeComponentSchema.cpp:33`","engine":"5.3-5.8","ue":["5.3","5.4","5.5","5.6","5.7","5.8"],"bl":[],"section":"UE StateTree property bindings - what the compile requires and what the binding widget offers"},{"id":"U527","fact":"**A PIE-world UObject that an editor transaction records (Python run inside a scoped transaction that calls a UFunction on, or modifies, a PIE widget) is kept alive by UTransBuffer after PIE ends, and UEditorEngine::EndPlayMap then asserts \"Object 'GameInstance_N' from PIE level still referenced\". The editor dies.** The reference chain printed in the log runs root -> UnrealEdEngine::Trans (TransBuffer) -> FTransaction::FObjectRecord -> the PIE widget -> its WidgetTree -> its outer GameInstance. Calls that mutate PIE objects must run outside any editor transaction.","source":"measured 2026-10-12 on 5.8.2 (crash log of the assert, reference chain via FReferenceChainSearch::FindAndPrintStaleReferencesToObjects); 5.8 `Editor/UnrealEd/Private/PlayLevel.cpp:553`; 5.8 `Editor/UnrealEd/Private/EditorTransaction.cpp:611`; 5.8 `Runtime/CoreUObject/Private/Misc/TransactionCommon.cpp:152`","engine":"5.8","ue":["5.8"],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"},{"id":"U641","fact":"**A C++ `UPROPERTY(meta = (BindWidgetOptional))` widget member with no BlueprintReadOnly/ReadWrite cannot be read by a Get node in the Widget Blueprint that binds it, and `UUserWidget::GetWidgetFromName` is not Blueprint-callable, so a graph has no route to such a widget by reference or by name.**","source":"Live, 2026-10-20: UE 5.8.3, a WBP child of a C++ UUserWidget with `TObjectPtr<UWidget> Pick` declared `UPROPERTY(meta = (BindWidgetOptional))`: MifBridge `add_variable_get` refused \"property 'Pick' ... is not Blueprint-visible\"; `add_function_call GetWidgetFromName cls UserWidget` refused \"function 'GetWidgetFromName' not found\". Re-run: the same two ops on any such WBP.","engine":"","ue":[],"bl":[],"section":"UE generic routes settled live - create_asset, set_property, edit_container, call_function, evaluate_sequence (UE 5.8.2 scratch editor, 2026-10-04)"}]