Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions unreal/LEARNINGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,33 @@ No secrets - reference credential *locations*, never paste them.
clean on UE 5.8 (496/503 packages, 0 err/warn) using the same ZenServer/
`FShaderJobCache` grammar - confirming the fix generalizes rather than being
tuned to one project's log shape.
- **An installed (binary) engine refuses a target that changes shared build settings, and
UBT's own suggested fix is the trap.** A `.Target.cs` whose `DefaultBuildSettings` /
`IncludeOrderVersion` differ from the installed engine fails in seconds, before compiling
anything: `<Target> modifies the values of properties: [ <Prop>: A != B ]. This is not
allowed, as <Target> has build products in common with UnrealEditor`, with
`CompilationResult=6`. UBT suggests `BuildEnvironment = TargetBuildEnvironment.Unique`,
which means "compile the engine too" and is impossible on an installed or container
engine. Align the target to the engine instead. Now both a signature row and a preflight
check, since it is cheap to spot in `Source/*.Target.cs` before a build starts.
- **One missing `#include` masquerades as missing types *and* as broken inheritance.**
Headers using a shared struct header without including it produced `unknown type name
'<FStruct>'` for a struct that **is** defined, `cannot initialize object parameter of
type '<Base>'`, and a `TIsDerivedFrom<...>::IsDerived` static_assert against a class that
**does** derive from that base. Two includes collapsed ~30 of 57 error lines. The durable
lesson is ordering: fix includes and rebuild *before* believing any "unknown type" or
"not derived from" claim.
- **UHT cannot parse a nested enum used in a `UFUNCTION` signature**, and says so
misleadingly: `Unable to find 'class', 'delegate', 'enum', or 'struct' with name 'X'`
paired with `C++ Default parameter not parsed`. The type is usually a few lines above,
declared inside the UCLASS. Hoist it to file scope and tag it `UENUM()`.
- **`Error_UnknownCookFailure` (25) is not always missing content.** A Blueprint-only
template project on a *matched* engine failed with `Could not find a function named "..."
in 'X'` and `In use pin ... no longer exists on node`, plus `Failed to find script package
for import object 'Package /Script/<Plugin>'` - Blueprint nodes outliving a plugin no
longer enabled by default. When the missing import is a `/Script/<Plugin>` package rather
than a `/Game/` asset, check plugin enablement, not an unsynced file. Related preflight: a
project with no `.umap`/`.uasset` has nothing to cook at all - compile it instead.

## B. Operational / rig learnings (standing up a real-engine test rig)

Expand Down Expand Up @@ -91,3 +118,29 @@ No secrets - reference credential *locations*, never paste them.
was roughly a third smaller than the full one, still shipped a working compiler
toolchain and editor, and only dropped debug symbols - a good default unless
you specifically need those symbols.
9. **A WSL2 `df` reports free space that does not exist.** The WSL ext4 volume is a sparse
virtual disk on the Windows drive: `df` inside WSL showed ~895 GB free while the host
volume had **19 GB**. Disk prechecks on a WSL-hosted rig must read the Windows volume
(`df /mnt/c`), never the WSL root, or a cook sized against the WSL number fills the host
drive.
10. **Windows OpenSSH kills detached children when the SSH session closes** - the
Windows-side counterpart to item 5. A hidden `Start-Process` launched over SSH appears
to start, then silently produces nothing. Launch long jobs as scheduled tasks
(`schtasks /Create ... /SC ONCE /RL HIGHEST`, then `schtasks /Run`), which outlive the
session.
11. **A dead registry credential is indistinguishable from a missing tag, and image
preflights need bounds.** With an expired token, `docker manifest inspect` returns
`denied: denied` for *every* tag, including images already present locally - which reads
as "this tag does not exist" and produced a wrong conclusion about which engine images
are published. Verify auth against a known-present image before calling any tag absent.
`docker login` also writes to the **invoking user's** config, so automation running as
another user keeps its own stale token - point at the good config with `DOCKER_CONFIG=`
rather than copying the credential (see item 3). And bound the check itself: `docker
image inspect` is normally instant but blocks on the content-store lock while a
multi-GB layer commits, and an SSH failure (255) or timeout (124) says **nothing** about
whether the image exists - never report those as "image missing".
12. **Windows sshd ignores the per-user `authorized_keys` for administrators.** With the
default `Match Group administrators` block it reads only
`%ProgramData%\ssh\administrators_authorized_keys`; a per-user key is silently ignored
and fails as a plain `Permission denied (publickey)`. That file also needs inheritance
removed and its ACL limited to SYSTEM + Administrators.
9 changes: 9 additions & 0 deletions unreal/skills/unreal-build/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ beats a re-cook; a FAST_COOK tracer (§2) beats a full pipeline (§3).
parses cleanly.
4. **Capture the log**: pipe through `tee <output>/cook_output_<id>.log` - the
metrics and triage in `unreal-observe` §7 read from it.
5. **Is there anything to cook?** If the project has **no `.umap`/`.uasset`** (a
code-only or scaffold project whose Content is placeholders), a cook has nothing
to do and its failure teaches nothing - use §5 UBT compile instead. Check before
proposing a cook, not after it fails.
6. **On an installed (binary) engine, check target build settings match.** An editor
target that sets `DefaultBuildSettings`/`IncludeOrderVersion` differently from the
installed engine is refused outright by UBT in seconds (`unreal-observe` §8). Read
them out of `Source/*.Target.cs` during the doctor pass, while a mismatch is still
cheap to spot.

## 1. Invocation forms - Windows vs Linux/Mac

Expand Down
3 changes: 3 additions & 0 deletions unreal/skills/unreal-observe/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ don't manufacture findings. Otherwise map these distinctive strings straight to
| Cook: `GetLastError=206` · *"The filename or extension is too long"* · SavePackage *"Could not create file ..."* | Windows **MAX_PATH** (260) - see §6 | shorten the project root (only reliable fix); *count the failing path first* - under 260 ⇒ it's something else |
| `LogDerivedDataCache: Warning: ... unreachable ... falling back to Local` (usually with a later `Hit Rate: 0.x%`) | shared DDC backend down ⇒ every shader recompiled from source | restore DDC connectivity (DNS/firewall/service) before the next cook; this build's *output* is fine - don't re-run it, and don't kill a cook mid-compile |
| `LogUObjectGlobals`/`LogLinker`: *"Could not find file for package"* · *"Failed to load '<pkg>'"* · a dangling object-property reference, then `Can't cook <asset>` | a referenced content asset is missing from the workspace | on a Perforce team this is usually unsubmitted or unsynced - `p4 have <path>` (synced?), `p4 files <path>` (in depot at all?), `p4 opened` (an unsubmitted add?) - *then* hand the fix to the perforce plugin, or clear/repoint the reference via `unreal-editor-scripting` |
| UBT, in seconds, with **no** compilation: `<Target> modifies the values of properties: [ <Prop>: A != B ]. This is not allowed, as <Target> has build products in common with UnrealEditor` (`CompilationResult=6`) | the target's `.Target.cs` sets `DefaultBuildSettings` / `IncludeOrderVersion` differing from the **installed** engine's own build environment; shared build products cannot disagree | align `DefaultBuildSettings` / `IncludeOrderVersion` in `Source/*.Target.cs` to the engine actually in use. Do **NOT** apply UBT's own suggested `BuildEnvironment = TargetBuildEnvironment.Unique` - that's the trap: Unique requires an engine **source** build and is impossible on an installed or container engine |
| UHT: `Unable to find 'class', 'delegate', 'enum', or 'struct' with name 'X'` **paired with** `C++ Default parameter not parsed: <Param> 'X::<Value>'` | `X` is an `enum class` declared **nested inside** the UCLASS and used in a `UFUNCTION` signature - UHT does not parse nested UENUMs | hoist the enum to **file scope** and tag it `UENUM()`. The "unable to find" wording is misleading - the type is usually a few lines above the error, just not where UHT can see it |
| `unknown type name '<FStruct>'` where the struct **is** defined; and/or `cannot initialize object parameter of type '<Base>'`; and/or a `TIsDerivedFrom<..., <Base>>::IsDerived` static_assert on a class that **does** derive from `<Base>` | a missing `#include` of the header defining those types - one incomplete type cascades into bogus "missing type" and "wrong base class" errors | grep for the definition before believing the error. Add the `#include`, then **rebuild before triaging anything else** - include cascades routinely account for half the error lines in a run |

**Exit codes** name *which stage* failed; always pair the code with the first
`Error:`/`Fatal:` line, which names *why*: AutomationTool `0` = success, `25` =
Expand Down
Loading