WidgetForge¶
Summary: WidgetForge is the project's agent-drivable UMG authoring pipeline: declarative JSON specs compile into real WidgetBlueprints (
WidgetForge.Apply), WidgetBlueprints export back into canonical specs (WidgetForge.Export), and widgets render headlessly to PNG for visual verification (WidgetForge.Render). A staleness gate makes human designers and agents safe co-authors of the same widgets: the widget asset is the source of truth, and Apply refuses to overwrite designer work it hasn't seen.
Why it exists¶
AI agents cannot drive the UMG Designer (drag-drop Slate UI), and UE Python cannot author widget
trees (WidgetBlueprint.widget_tree is not reflected). The MCP bridge also blocks py console
commands, so WidgetForge ships console commands backed by an editor-module C++ compiler
(Source/ProjectEternalEditor/{Public,Private}/WidgetForge/). Prior-art survey (2026-07): three
independent UE tools converged on the same JSON→regenerate architecture; none solved two-writer
co-authoring. WidgetForge's model (export-first refresh + refuse-on-drift) follows the patterns
that survived elsewhere (Slint, Android Studio layouts, W3C design tokens).
The co-authoring model (widget-as-truth)¶
Two writers touch the same widgets: a human in the UMG designer and an agent through specs. The contract that keeps them safe:
- The widget asset is the source of truth. Specs in
Tools/WidgetForge/specs/are a full-fidelity, refreshable text mirror (and give binary UI assets reviewable git diffs). - Agents work Export-first:
Export(refresh spec from widget) → edit spec →Apply. - The staleness gate makes the contract self-enforcing. Export stamps a tool-owned
_metablock (sourceHash= hash of the widget's canonical tokenless export). Apply recomputes the hash of the current widget and refuses withstatus:"stale"on mismatch — a forgotten Export is a loud failure, never a silent clobber of designer edits.-Forceoverrides (audited asforcedOverwrite:true). After a successful Apply the spec's_metais updated in place. - Apply rebuilds the widget tree from scratch (deterministic, idempotent) but never touches
animations or the event graph; variable GUIDs are preserved by name, so bindings survive.
Renames need
"renamedFrom"on the node (one Apply) — GUIDs carry forward and animation bindings/graph references are fixed up; unmarked disappearing variables warn loudly. - Steady state: the designer opens any spec-managed WBP, edits anything, saves — done. The next agent Export folds those edits into the spec.
What can't round-trip (guarded, not silent): named-slot content (named_slots_unsupported
refusal), tree-internal object pointers (export warns + skips). Everything else — any CPF_Edit
property differing from its CDO, slots, classDefaults (diffed against the ParentClass CDO),
tokens — round-trips byte-identically.
Commands¶
WidgetForge.Apply <SpecJsonPath> [-Force] # build/update WBP from spec (staleness-gated)
WidgetForge.Export <AssetPath> <OutJsonPath> [<TokensPath>] # widget -> canonical spec + _meta
WidgetForge.Render <AssetPath> <W> <H> <OutPngPath> # headless PNG (no PIE needed)
WidgetForge.RoundTrip <AssetPath> [<W> <H>] # acceptance oracle: spec + pixel identity
WidgetForge.ApplyStyles <StylesJsonPath> # CommonTextStyle-style Blueprint assets
Every call writes a JSON report to Saved/WidgetForge/last_result.json — always read it.
Machine-readable status: ok, error, stale, pie_blocked, token_lint_failed,
named_slots_unsupported, load_failed. All commands refuse during PIE (editor asset loading is
disabled in play mode; pre-guard, Apply's create-path opened a modal "Overwrite Existing Object"
dialog that silently blocked the game thread — the historical "MCP deadlock").
Apply wall-clock is ~0.9 s regardless of widget size (compile+save dominates; measured ×10 on a 5-widget and a 48-widget tree, 2026-07-02).
Tokens and styles¶
Tools/WidgetForge/tokens.json is the design-token source ("$token" in specs). Semantic
duplicates must be aliases ("era-arcanum": "$arcane"); Apply/Export lint the file and fail
on ambiguous duplicates, unknown refs, and cycles. Export reverse-maps values to $token/hex.
Shared text looks live in style Blueprint assets (TextStyles.styles.json via ApplyStyles) —
a surface that is simultaneously designer-native (Details panel) and agent-native (JSON).
Format reference¶
The spec format itself (widget nodes, slots, shorthands, CSS→UMG conversion rules, hard-won
caveats) is documented in Tools/WidgetForge/SPEC_FORMAT.md. Design history and rejected
alternatives (merge-based Apply, spec-as-truth, denylists):
ImplementationDocs/WidgetForge_DesignerCoAuthoring.plan.md.