feat(skills): ship ue-design-skills bundle, licensing and delivery gate
Phase 0 of the handoff plan, as a marketplace rather than a flat skills/ directory. Content moved out of the LyraResearch archive and depersonalised: addresses stay in the archive, recipes ship. - plugins/ue-design-skills: 17 skills, 232 failure-mode entries, each with the six required fields; catalog.json as the harness-neutral source of truth and .claude-plugin/ as one adapter over it. - _gate: 16 rules, one poisoned fixture per rule, plus surface coverage so a declared file cannot silently miss the line rules. - ADR-0002 (harness-neutral bundle behind a marketplace) and ADR-0003 (split licensing: CC BY-ND 4.0 prose, Apache-2.0 code and metadata). - LICENSE files at both levels, CONTRIBUTING.md, docs/licensing-options.md as the material the licence decision grew from. Verified: gate.py 0 violations; test_gate.py 16/16 rules redden on their fixtures with a clean baseline and 2 root files reaching the line rules. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+202
@@ -0,0 +1,202 @@
|
||||
# Patterns: platform budgets in a measured reference product
|
||||
|
||||
A worked reading of one shipped platform-budget configuration: scalability
|
||||
buckets, device profile tiers, streaming pools and the code that applies them.
|
||||
|
||||
This file is almost entirely about **configuration**, which makes it unlike the
|
||||
others in this bundle in one important way: none of the findings below are bugs
|
||||
in the usual sense. Every value is valid, every file parses, and the game runs.
|
||||
What the audit produced is a list of places where the configuration says
|
||||
something the runtime does not do — and the only reason any of it is findable is
|
||||
that someone read the engine's own configuration alongside the project's.
|
||||
|
||||
Markers: **[measured]** — read in project or engine configuration and source;
|
||||
**[derived]** — conclusion from measured facts; **[open]** — requires hardware.
|
||||
|
||||
**No number here is a memory measurement.** Nothing was profiled. Everything is
|
||||
a declared budget, which is exactly the distinction the skill is about.
|
||||
|
||||
---
|
||||
|
||||
## 1. The single most useful table in the audit
|
||||
|
||||
What the project declares about the texture streaming pool, per mobile tier
|
||||
**[measured]**:
|
||||
|
||||
| Tier | Pool declared by the project | Effective source |
|
||||
|---|---:|---|
|
||||
| Low | — | engine scalability bucket |
|
||||
| **Mid** | **85 MB** | **device profile** |
|
||||
| High | — | engine scalability bucket |
|
||||
| Epic | — | engine scalability bucket |
|
||||
|
||||
The project sets the pool in exactly two lines — the mid tier of each mobile
|
||||
platform — and declares **no** per-tier texture-quality sections of its own
|
||||
**[measured]**. Every other tier inherits engine values, which on this engine
|
||||
version run from several hundred megabytes to a gigabyte, with the
|
||||
video-memory clamp disabled at the top **[measured]**.
|
||||
|
||||
**[derived]** Two consequences, and the second is the one that matters:
|
||||
|
||||
1. The tier with the smallest declared budget is the only tier the project
|
||||
actually configured. The rest are engine defaults that nobody chose.
|
||||
2. Because a device profile applies at a higher priority than scalability, the
|
||||
mid tier's pool **cannot be changed by the in-game texture setting**, while
|
||||
on every other tier the same setting works. One control, opposite behaviour,
|
||||
no explanation anywhere in the UI.
|
||||
|
||||
That asymmetry is the reason ST-01 leads the failure modes. It is not a mistake
|
||||
in either file; it is a mistake that only exists between them.
|
||||
|
||||
---
|
||||
|
||||
## 2. Priority, in one paragraph you can act on
|
||||
|
||||
Device profiles apply variables whose names carry the scalability prefix at
|
||||
**scalability** priority, and every other variable at **device-profile**
|
||||
priority — which outranks everything the settings system does **[measured, from
|
||||
engine source]**.
|
||||
|
||||
**[derived]** So the same file produces two completely different mutability
|
||||
outcomes depending on a naming convention:
|
||||
|
||||
- a prefixed quality bucket set in a profile is a **default** the player can
|
||||
override;
|
||||
- an unprefixed variable set in a profile is a **lock**.
|
||||
|
||||
Nothing in the ini syntax distinguishes them. The audited project uses both
|
||||
forms, mostly correctly and once consequentially (§1).
|
||||
|
||||
**The practical rule:** you cannot determine effective configuration by reading
|
||||
files. Dump the variables with their source on the target device. That command is
|
||||
the only authoritative answer, and it takes ten seconds.
|
||||
|
||||
---
|
||||
|
||||
## 3. Automatic detection promotes on the wrong axis
|
||||
|
||||
Promotion thresholds are expressed against a GPU performance index, for every
|
||||
quality group, with **no memory term anywhere** **[measured]**. The texture group
|
||||
reaches its top level at a low index value, and that top level is the one where
|
||||
the engine disables the video-memory clamp on the pool **[measured]**.
|
||||
|
||||
**[derived]** A fast chip with modest memory therefore lands in the bucket that
|
||||
grants the largest pool and removes the guard against it. The two decisions were
|
||||
made in different files by different reasoning, and each is defensible alone.
|
||||
|
||||
The project's own comment on the thresholds is honest about their intent — tuning
|
||||
so a specific generation of hardware lands in a specific mix **[measured]** — and
|
||||
that intent is about throughput, which is exactly the axis that misses this.
|
||||
Recipe: ST-02.
|
||||
|
||||
---
|
||||
|
||||
## 4. Dead configuration, and how much of it there is
|
||||
|
||||
Four independent instances, all silent, all reviewed at some point:
|
||||
|
||||
| Finding **[measured]** | What it means |
|
||||
|---|---|
|
||||
| A top-tier device profile section defined in full, absent from the profile registry and from the engine's base profiles | Every value in it is unreachable |
|
||||
| Comment banners grouping devices under tier labels that contradict the base profile named two lines below | The documentation and the behaviour disagree; several devices sit under the wrong banner |
|
||||
| A user-facing profile variant list read in three places and set in no configuration file | The profile override never applies; the console preset UI never appears |
|
||||
| Optional mip levels configured in every mobile texture group, with chunk acquisition disabled | A high-resolution optional tier that cannot be delivered |
|
||||
|
||||
**[derived]** The common structure is worth naming, because it generalises far
|
||||
beyond streaming: **configuration that names a mechanism the project does not
|
||||
have.** It reads as a decision, survives review indefinitely, and produces
|
||||
nothing. It is the configuration-file form of the stub described in
|
||||
`ue-reference-project-adoption`.
|
||||
|
||||
Recipes: ST-05, ST-06, ST-07, ST-08.
|
||||
|
||||
---
|
||||
|
||||
## 5. Two arithmetic defects in the clamp layer
|
||||
|
||||
**A comparison between a percentage and a level.** A frame-rate compatibility
|
||||
check obtains a resolution-quality limit — a percentage defaulting to one hundred
|
||||
— and compares it against an overall quality level in the range zero to three
|
||||
**[measured]**. The comparison is always satisfied, so the constraint never
|
||||
binds.
|
||||
|
||||
**[derived]** What makes this findable rather than invisible is that the sibling
|
||||
function two hundred lines away uses the correct accessor for the same purpose
|
||||
**[measured]**. One of the two is wrong, and the codebase contains its own
|
||||
counterexample.
|
||||
|
||||
**A remap with no guard on its denominator.** A normalisation between two
|
||||
resolution ranges divides by the difference between a maximum and a minimum, with
|
||||
no check that they differ, and the degenerate case is reachable from device
|
||||
profile data **[measured]**.
|
||||
|
||||
Recipes: ST-09, ST-10. Both are three-line fixes and both would be caught by one
|
||||
unit test each — which is the argument for testing clamps at their range
|
||||
boundaries rather than at their typical values.
|
||||
|
||||
---
|
||||
|
||||
## 6. What the project got right
|
||||
|
||||
Worth stating, because the list above is long and the configuration is not bad:
|
||||
|
||||
1. **Cook-time budget levers are used deliberately.** Mobile texture groups clamp
|
||||
the maximum size sixteen-fold on a side, animation key stripping is enabled,
|
||||
and unused detail-mode components and emitters are pruned at cook
|
||||
**[measured]**. These are budget-A decisions, correctly separated from
|
||||
runtime budgets.
|
||||
2. **Foliage density was moved between scalability groups deliberately**, with the
|
||||
old group's keys removed and a different curve applied in the new one
|
||||
**[measured]**. That is a real understanding of how the groups compose.
|
||||
3. **Per-device resolution overrides on specific hardware** rather than a blanket
|
||||
platform value **[measured]**.
|
||||
4. **Dynamic resolution configured for one mobile platform** with an explicit
|
||||
comment stating the target frame rate and output resolution **[measured]** —
|
||||
the kind of comment that makes a number reviewable.
|
||||
5. **A rich set of runtime diagnostics** already wired: the profile selection
|
||||
logs its inputs and its chosen result, frame pacing logs its target, quality
|
||||
clamping logs both the before and after values **[measured]**. Someone
|
||||
arriving on a device has the answers available.
|
||||
|
||||
**[derived]** Point 5 deserves emphasis. Most of the findings in this file are
|
||||
detectable in ten seconds on a device by reading log lines the project already
|
||||
prints. The failure is not instrumentation; it is that nobody read the output.
|
||||
|
||||
---
|
||||
|
||||
## 7. What to copy, and what to check first
|
||||
|
||||
Copy: the four-budget separation as a documentation convention; cook-time levers
|
||||
used per platform; the diagnostic logging around profile selection and clamping;
|
||||
per-device overrides for outlier hardware.
|
||||
|
||||
Check first, in this order, on your own project:
|
||||
|
||||
1. **Dump variables with their source** on each target. Everything else is
|
||||
inference. (ST-01)
|
||||
2. **Diff declared profiles against defined sections.** One command. (ST-05)
|
||||
3. **Separate reads from writes** for every data-driven selection list. (ST-07)
|
||||
4. **Unit-test every clamp** at both ends of its range. (ST-09, ST-10)
|
||||
5. **Check both pool values** — texture and mesh — on every platform. (ST-03)
|
||||
|
||||
**[derived]** Steps 1 and 2 take minutes and would have found the two largest
|
||||
findings in this file. That ratio is the argument for doing configuration audits
|
||||
at all.
|
||||
|
||||
---
|
||||
|
||||
## Provenance
|
||||
|
||||
Measured against Epic's Lyra Starter Game on Unreal Engine 5.6, read as project
|
||||
configuration and source, cross-referenced against the engine's own scalability
|
||||
and device-profile configuration and streaming source in the same installation.
|
||||
Source addresses and engine paths stay in the research archive that produced this
|
||||
skill; each `ST-` identifier resolves back to the audited location there.
|
||||
|
||||
## Evidence boundary
|
||||
|
||||
**Nothing here was profiled.** No device was measured, no memory figure is an
|
||||
observation, and every quantity is a declared budget or a configured threshold.
|
||||
Engine values describe one engine version and will drift. The recipes exist
|
||||
precisely because these numbers must be re-derived on your project and your
|
||||
hardware before they mean anything.
|
||||
Reference in New Issue
Block a user