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:
ue-toolchain
2026-09-05 23:48:55 +07:00
parent 9e2194c298
commit dab3f35079
67 changed files with 21630 additions and 0 deletions
@@ -0,0 +1,4 @@
{
"name": "sample-plugin",
"description": "Gate fixture manifest. Clean baseline, not shipped."
}
@@ -0,0 +1,5 @@
# Gate fixture: clean baseline
A minimal plugin tree that satisfies every delivery rule. The gate must report
zero violations here. Each poisoned variant in `test_gate.py` is this tree plus
exactly one documented mutation.
@@ -0,0 +1,14 @@
{
"schema": "ue-skills-catalog/1",
"name": "sample-plugin",
"description": "Gate fixture catalog. Clean baseline, not shipped.",
"skills": [
{
"id": "sample-skill",
"path": "skills/sample-skill",
"entry": "skills/sample-skill/SKILL.md",
"description": "Gate fixture skill proving the catalog contract holds.",
"use_when": "Never in production; this entry exists so the baseline has one valid row."
}
]
}
@@ -0,0 +1,25 @@
---
name: sample-skill
description: >-
Gate fixture. A minimal clean skill bundle that proves the delivery gate
accepts valid content, including a donor name inside a Provenance section.
---
# Sample skill
> A reference project shows you a shape, not a contract.
## 1. Norm
State the field, name its consumer, and give a recipe that reproduces the check
in the reader's own tree. A norm that cannot be re-derived is decoration.
## Provenance
Measured on Epic's Lyra Starter Game for Unreal Engine 5.6. Source addresses
stay in the research archive; the recipes below reproduce each finding in your
own project. This section is the only place the donor may be named.
## 2. Failure modes
Detection recipes and guardrails: [failure modes](references/failure-modes.md).
@@ -0,0 +1,37 @@
# Failure modes: sample skill
Gate fixture. One complete entry, in the exact shape the gate accepts.
An entry is valid only if a reader who has never seen the donor project can run
`Detect` in their own tree and get an answer. The three middle fields are the
ones that cannot be written by someone who did not do the work.
---
### SS-01 - Editable property with no writer
**Mechanism.** A tunable float is compared against a timestamp field, but the
timestamp is never assigned anywhere in the codebase.
**Why it is silent.** Time since an unset stamp is time since world start, which
always exceeds the threshold. The comparison is permanently true, so the gate it
was meant to impose never rejects anything and no error is ever raised.
**Why the obvious check misses it.** The field is read twice and participates in
a comparison, so any "is this used?" search answers yes. What is absent is the
**writer**, not the reader.
**Symptom.** A designer tunes the property for days and reports that it changes
nothing.
**Detect.** For each editable property, search for an assignment rather than a
mention, and subtract the declaration -- a member declared with an initializer
matches every assignment pattern you can write:
rg -n "\bPropertyName\b\s*(=|\+=|-=)" Source/ \
| rg -v "\b(bool|u?int\d+|float|double|F[A-Z]\w+)\s+PropertyName\b"
If reads exist and this comes back empty, the property is a stub.
**Guardrail.** Do not ship an editor-exposed property whose value has no writer.
Every tunable needs one owning assignment site and one test that moves it.