Release 1.1.2 — the default path works end to end

Fix release for 1.1.1: the minimal path end to end, engine commands that
run on Godot, Unity and Unreal, hook warnings you can see, and verdicts
that say when they could not check. Details in CHANGELOG.md; upgrade
steps in UPGRADING.md (v1.1.1 → v1.1.2).

Fixes #67
Fixes #131

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WuJn2FdynEybfL5KC697nJ
This commit is contained in:
Donchitos
2026-09-29 21:32:19 +10:00
co-authored by Claude Opus 5.5
parent 7ed2c3e9c4
commit b21fa0f7f2
383 changed files with 22888 additions and 10618 deletions
@@ -3,7 +3,6 @@
## Agent Summary
Domain: Addressable Asset System — groups, async loading/unloading, handle lifecycle management, memory budgeting, content catalogs, and remote content delivery.
Does NOT own: rendering systems (engine-programmer), game logic that uses the loaded assets (gameplay-programmer).
Model tier: Sonnet (default).
No gate IDs assigned.
---
@@ -11,8 +10,8 @@ No gate IDs assigned.
## Static Assertions (Structural)
- [ ] `description:` field is present and domain-specific (references Addressables / asset loading / content catalogs / remote delivery)
- [ ] `allowed-tools:` list includes Read, Write, Edit, Bash, Glob, Grep
- [ ] Model tier is Sonnet (default for specialists)
- [ ] `tools:` list includes Read, Write, Edit, Bash, Glob, Grep
- [ ] Model tier is `sonnet` — frontmatter `model:` reads exactly `sonnet` (tiers: `.claude/docs/model-tiers.md`)
- [ ] Agent definition does not claim authority over rendering systems or gameplay using the loaded assets
---
@@ -26,8 +25,8 @@ No gate IDs assigned.
- Stores the returned `AsyncOperationHandle<Texture2D>` in the requesting object
- On character destruction (`OnDestroy()`), calls `Addressables.Release(handle)` with the stored handle
- Does NOT use `Resources.Load()` as the loading mechanism
- Notes that releasing with a null or uninitialized handle causes errors — includes a validity check
- Notes the difference between releasing the handle vs. releasing the asset (handle release is correct)
- Checks that the load succeeded (`handle.Status`) before using `Result`, and never releases a handle it does not hold (e.g., a character destroyed before its load was started)
- Uses `Addressables.Release(handle)` for loaded assets and reserves `Addressables.ReleaseInstance()` for `InstantiateAsync` results — does not mix the two
### Case 2: Out-of-domain redirect
**Input:** "Implement the rendering system that applies the loaded texture to the character mesh."
@@ -35,37 +34,39 @@ No gate IDs assigned.
- Does NOT produce rendering or mesh material assignment code
- Explicitly states that rendering system implementation belongs to `engine-programmer`
- Redirects the request to `engine-programmer`
- May describe the asset type and API surface it will provide (e.g., `Texture2D` reference once the handle completes) as a handoff spec
- Hands off the asset contract instead: the `Texture2D` comes from the handle's `Result` once the load succeeds, and stays valid only until the owning handle is released, so the consumer must not hold it past that point
### Case 3: Memory leak — un-released handle
**Input:** "Memory usage keeps climbing after each level load. We use Addressables to load level assets."
**Expected behavior:**
- Diagnoses the likely cause: `AsyncOperationHandle` objects not being released after use
- Identifies the handle leak pattern: loading assets into a local variable, losing reference, never calling `Addressables.Release()`
- Produces an auditing approach: search for all `LoadAssetAsync` / `LoadSceneAsync` calls and verify matching `Release()` calls
- Provides a corrected pattern using a tracked handle list (`List<AsyncOperationHandle>`) with a `ReleaseAll()` cleanup method
- Does NOT assume the leak is elsewhere without evidence
- Produces an auditing approach: pair every `LoadAssetAsync` with `Release()`, every `InstantiateAsync` with `ReleaseInstance()`, and every `LoadSceneAsync` with `UnloadSceneAsync()`
- Provides a corrected pattern: every active handle kept in a tracked collection and released when the level unloads, so nothing accumulates across level transitions
- Confirms the diagnosis with the Addressables Event Viewer (per-asset reference counts) or the Memory Profiler before concluding — does not assume the leak is elsewhere without evidence
### Case 4: Remote content delivery — catalog versioning
**Input:** "We need to support downloadable content updates without requiring a full app re-install."
**Expected behavior:**
- Produces the remote catalog update pattern:
- Produces the remote catalog update pattern documented in `docs/engine-reference/unity/plugins/addressables.md`:
- `Addressables.CheckForCatalogUpdates()` on startup
- `Addressables.UpdateCatalogs()` for detected updates
- `Addressables.DownloadDependenciesAsync()` to pre-warm the updated content
- Notes catalog hash checking for change detection
- Addresses the edge case: what happens if a player starts a session, the catalog updates mid-session — defines behavior (complete current session on old catalog, reload on next launch)
- `Addressables.GetDownloadSizeAsync()` before downloading, then `Addressables.DownloadDependenciesAsync()` to pre-download the updated content
- Re-downloads only changed bundles; catalogs are versioned so clients fall back to cached content, and download failures retry with exponential backoff while showing progress
- Raises the mid-session case (a catalog update lands while a session is running) and asks the user which behavior they want, rather than silently picking one
- Plans the update-path tests: fresh install, V1 to V2, and V1 to V3 skipping V2
- Does NOT design the server-side CDN infrastructure (defers to devops-engineer)
### Case 5: Context pass — platform memory constraints
**Input:** Platform context: Nintendo Switch target, 4GB RAM, practical asset memory ceiling 512MB. Request: "Design the Addressables loading strategy for a large open-world level."
**Expected behavior:**
- References the 512MB memory ceiling from the provided context
- References the 512MB memory ceiling from the provided context, and uses it in place of its own default console budget
- Designs a streaming strategy:
- Divide the world into addressable zones loaded/unloaded based on player proximity
- Defines a memory budget per active zone (e.g., 128MB, max 4 zones active)
- Specifies async pre-load trigger distance and unload distance (hysteresis)
- Notes Switch-specific constraints: slower load times from SD card, recommend pre-warming adjacent zones
- Additive scene streaming through `Addressables.LoadSceneAsync()` / `Addressables.UnloadSceneAsync()`, with groups organized per zone by loading context
- A memory budget per active zone whose total stays under the 512MB ceiling, with zones unloaded on transition rather than accumulated
- Essential zone content loaded first, optional content streamed after
- Defers where zone boundaries fall to `level-designer`
- Flags storage I/O speed on the target hardware as a load-time risk to verify on device, without quoting platform load speeds from memory
- Does NOT produce a loading strategy that would exceed the stated 512MB ceiling without flagging it
---
@@ -76,8 +77,9 @@ No gate IDs assigned.
- [ ] Redirects rendering and gameplay asset-use code to engine-programmer and gameplay-programmer
- [ ] Returns structured output (loading patterns, handle lifecycle code, streaming zone designs)
- [ ] Always pairs `LoadAssetAsync` with a corresponding `Release()` — flags handle leaks as a memory bug
- [ ] Designs loading strategies against provided memory ceilings
- [ ] Does not design CDN/server infrastructure — defers to devops-engineer for server side
- [ ] Designs loading strategies against provided memory ceilings, which override its default per-platform budgets
- [ ] Specifies only the client-side contract (remote load path / URL structure, catalog versioning, cache-header requirement) and defers CDN hosting and the delivery pipeline to devops-engineer
- [ ] Asks "May I write this to [filepath]?" naming the file before writing
---
@@ -3,7 +3,6 @@
## Agent Summary
Domain: ECS architecture (IComponentData, ISystem, SystemAPI), Jobs system (IJob, IJobEntity, Burst), Burst compiler constraints, DOTS gameplay systems, and hybrid renderer.
Does NOT own: MonoBehaviour gameplay code (gameplay-programmer), UI implementation (unity-ui-specialist).
Model tier: Sonnet (default).
No gate IDs assigned.
---
@@ -11,8 +10,8 @@ No gate IDs assigned.
## Static Assertions (Structural)
- [ ] `description:` field is present and domain-specific (references ECS / Jobs / Burst / IComponentData)
- [ ] `allowed-tools:` list includes Read, Write, Edit, Bash, Glob, Grep
- [ ] Model tier is Sonnet (default for specialists)
- [ ] `tools:` list includes Read, Write, Edit, Bash, Glob, Grep
- [ ] Model tier is `sonnet` — frontmatter `model:` reads exactly `sonnet` (tiers: `.claude/docs/model-tiers.md`)
- [ ] Agent definition does not claim authority over MonoBehaviour gameplay or UI systems
---
@@ -25,10 +24,10 @@ No gate IDs assigned.
- Produces:
- `PlayerMovementData : IComponentData` struct with velocity, speed, and input vector fields
- `PlayerMovementSystem : ISystem` with `OnUpdate()` using `SystemAPI.Query<>` or `IJobEntity`
- Bakes the player's initial state from an authoring MonoBehaviour via `IBaker`
- Bakes the player's initial state from an authoring MonoBehaviour via a `Baker<T>` subclass (the pattern in `docs/engine-reference/unity/plugins/dots-entities.md`)
- Uses `RefRW<LocalTransform>` for position updates (not deprecated `Translation`)
- Marks the job `[BurstCompile]` and notes what must be unmanaged for Burst compatibility
- Does NOT modify the input polling system — reads from an existing `PlayerInputData` component
- Keeps one concern per system: the movement system reads input from a component (e.g. `PlayerInputData`) that a separate input system writes, rather than polling input itself, and asks before creating or changing that input side
### Case 2: MonoBehaviour push-back
**Input:** "Just use MonoBehaviour for the player movement — it's simpler."
@@ -36,7 +35,7 @@ No gate IDs assigned.
- Acknowledges the simplicity argument
- Explains the DOTS trade-off: more setup upfront, but the ECS/Burst approach provides the performance characteristics documented in the project's ADR or requirements
- Does NOT implement a MonoBehaviour version if the project has committed to DOTS
- If no commitment exists, flags the architecture decision to `lead-programmer` / `technical-director` for resolution
- If no commitment exists, routes the MonoBehaviour-vs-DOTS decision to `unity-specialist` (its named contact for overall Unity architecture), with `technical-director` as the escalation for an unresolved technical conflict
- Does not make the MonoBehaviour vs. DOTS decision unilaterally
### Case 3: Burst-incompatible managed memory
@@ -45,27 +44,26 @@ No gate IDs assigned.
- Flags `List<T>` as a managed type that is incompatible with Burst compilation
- Does NOT approve the Burst job with managed memory access
- Provides the correct replacement: `NativeArray<EnemyData>`, `NativeList<EnemyData>`, or `NativeHashMap<>` depending on the use case
- Notes that `NativeArray` must be disposed explicitly or via `[DeallocateOnJobCompletion]`
- Notes that the native container must be disposed, with the allocator chosen by lifetime (`Allocator.TempJob` for frame-scoped, `Allocator.Persistent` for long-lived) and capacity pre-allocated when the size is known
- Produces the corrected job using unmanaged native containers
### Case 4: Hybrid access — DOTS system needs MonoBehaviour data
**Input:** "The DOTS movement system needs to read the camera transform managed by a MonoBehaviour CameraController."
**Expected behavior:**
- Identifies this as a hybrid access scenario
- Provides the correct hybrid pattern: store the camera transform in a singleton `IComponentData` (updated from the MonoBehaviour side each frame via `EntityManager.SetComponentData`)
- Alternatively suggests the `CompanionComponent` / managed component approach
- Does NOT access the MonoBehaviour from inside a Burst job — flags that as unsafe
- Provides the bridge code on both the MonoBehaviour side (writing to ECS) and the DOTS system side (reading from ECS)
- Does NOT put the `Transform` or the MonoBehaviour itself into a component — components hold no references to managed objects
- Carries the camera data across the boundary as unmanaged values (`Unity.Mathematics` types) in an `IComponentData`, written at one explicit hand-off point on the MonoBehaviour side and read by the system — not reached for per entity
- Does NOT access the MonoBehaviour from inside a Burst job — flags managed access in Burst code as a compile error
### Case 5: Context pass — performance targets
**Input:** Technical preferences from context: 60fps target, max 2ms CPU script budget per frame. Request: "Design the ECS chunk layout for 10,000 enemy entities."
**Expected behavior:**
- References the 2ms CPU budget explicitly in the design rationale
- Designs the `IComponentData` chunk layout for cache efficiency:
- Groups frequently-queried together components in the same archetype
- Separates rarely-used data into separate components to keep hot data compact
- Estimates entity iteration time against the 2ms budget
- Provides memory layout analysis (bytes per entity, entities per chunk at 16KB chunk size)
- Splits components by system access pattern, not by game concept — no "god component"
- Keeps hot components small, with rarely-used data in separate components
- Uses tag components for filtering, avoids `ISharedComponentData` that would fragment archetypes, and uses `IEnableableComponent` instead of structural changes for toggled state
- Names how the budget will be verified — Entities Profiler system timings and per-archetype memory — instead of presenting unmeasured timings as fact
- Does NOT design a layout that will obviously exceed the stated 2ms budget without flagging it
---
@@ -74,14 +72,15 @@ No gate IDs assigned.
- [ ] Stays within declared domain (ECS, Jobs, Burst, DOTS gameplay systems)
- [ ] Redirects MonoBehaviour-only gameplay to gameplay-programmer
- [ ] Returns structured output (IComponentData structs, ISystem implementations, IBaker authoring classes)
- [ ] Returns structured output (IComponentData structs, ISystem implementations, `Baker<T>` authoring classes)
- [ ] Flags managed memory access in Burst jobs as a compile error and provides unmanaged alternatives
- [ ] Provides hybrid access patterns when DOTS systems need to interact with MonoBehaviour systems
- [ ] Designs chunk layouts against provided performance budgets
- [ ] Designs chunk layouts against provided performance budgets, and names the profiling step that verifies them
- [ ] Asks "May I write this to [filepath]?" naming the file before writing
---
## Coverage Notes
- ECS conversion (Case 1) must include a unit test using the ECS test framework (`World`, `EntityManager`)
- ECS conversion (Case 1) must include a unit test using the ECS test framework (`World`, `EntityManager`) under `Assets/Tests/EditMode/`
- Burst incompatibility (Case 3) is safety-critical — the agent must catch this before the code is written
- Chunk layout (Case 5) verifies the agent applies quantitative performance reasoning to architecture decisions
- Chunk layout (Case 5) verifies the agent designs against a stated budget and says how it will be measured, rather than asserting unmeasured numbers
@@ -3,7 +3,6 @@
## Agent Summary
Domain: Unity Shader Graph, custom HLSL, VFX Graph, URP/HDRP pipeline customization, and post-processing effects.
Does NOT own: gameplay code, art style direction.
Model tier: Sonnet (default).
No gate IDs assigned.
---
@@ -11,8 +10,8 @@ No gate IDs assigned.
## Static Assertions (Structural)
- [ ] `description:` field is present and domain-specific (references Shader Graph / HLSL / VFX Graph / URP / HDRP)
- [ ] `allowed-tools:` list includes Read, Write, Edit, Glob, Grep
- [ ] Model tier is Sonnet (default for specialists)
- [ ] `tools:` list includes Read, Write, Edit, Glob, Grep
- [ ] Model tier is `sonnet` — frontmatter `model:` reads exactly `sonnet` (tiers: `.claude/docs/model-tiers.md`)
- [ ] Agent definition does not claim authority over gameplay code or art direction
---
@@ -22,11 +21,9 @@ No gate IDs assigned.
### Case 1: In-domain request — appropriate output
**Input:** "Create an outline effect for characters using Shader Graph in URP."
**Expected behavior:**
- Produces a Shader Graph node setup description:
- Inverted hull method: Scale Normal → Vertex offset in vertex stage, Cull Front
- OR screen-space post-process outline using depth/normal edge detection
- Recommends the appropriate method based on URP capabilities (inverted hull for URP compatibility, post-process for HDRP)
- Notes URP limitations: no geometry shader support (rules out geometry-shader outline approach)
- Produces a Shader Graph node setup description (inverted hull via vertex offset, or a screen-space depth/normal edge detection), named to its convention (e.g., `SG_Char_Outline`) with reusable logic in a Sub Graph
- Stays in Shader Graph and drops to custom HLSL only if Shader Graph cannot achieve the effect; any HLSL keeps SRP Batcher compatibility (`UnityPerMaterial` CBUFFER)
- For a screen-space variant, uses a URP `ScriptableRendererFeature` / `ScriptableRenderPass` recorded through the RenderGraph API (`RecordRenderGraph`), not the deprecated `Execute` / CommandBuffer path — per `docs/engine-reference/unity/modules/rendering.md`
- Does NOT produce HDRP-specific nodes without confirming the render pipeline
### Case 2: Out-of-domain redirect
@@ -35,34 +32,33 @@ No gate IDs assigned.
- Does NOT produce UI implementation code
- Explicitly states that UI implementation belongs to `ui-programmer` (or `unity-ui-specialist`)
- Redirects the request appropriately
- May note that a shader-based fill effect for a health bar (e.g., a dissolve/fill gradient) is within its domain if the visual effect itself is shader-driven
- Any part it keeps is the shader-driven effect only (e.g., a dissolve or fill gradient on the bar) — it writes no health-bar UI code
### Case 3: HDRP custom pass for outline
**Input:** "We're on HDRP and want the outline as a post-process effect."
**Expected behavior:**
- Produces the HDRP `CustomPassVolume` pattern:
- C# class inheriting `CustomPass`
- `Execute()` method using `CoreUtils.SetRenderTarget()` and a full-screen shader blit
- Depth/normal buffer sampling for edge detection
- Produces the HDRP custom pass pattern its own standards name: a C# class inheriting `CustomPass`, run from a Custom Pass Volume, drawing a full-screen edge-detection shader that samples the depth/normal buffers
- Uses the HDRP overrides `docs/engine-reference/unity/current-best-practices.md` documents — `Setup(ScriptableRenderContext, CommandBuffer)`, `Execute(CustomPassContext ctx)`, `Cleanup()` — never URP's `RecordRenderGraph`, and marks any other HDRP member it uses (render-target helpers, `CustomPassContext` fields) unverified — nothing beyond those three is asserted from memory
- Notes that CustomPass requires HDRP package and does not work in URP
- Confirms the project is on HDRP before providing HDRP-specific code
### Case 4: VFX Graph performance — GPU event batching
### Case 4: VFX Graph performance — particle budget
**Input:** "The explosion VFX Graph has 10,000 particles per event and spawning 20 simultaneous explosions is causing GPU frame spikes."
**Expected behavior:**
- Identifies GPU particle spawn as the cost driver (200,000 simultaneous particles)
- Proposes GPU event batching: spawn events deferred over multiple frames, stagger initialization
- Recommends a particle budget cap per active explosion (e.g., 3,000 per explosion, queue excess)
- Notes the VFX Graph Event Batcher pattern and Output Event API for cross-frame distribution
- Sets a particle capacity limit per effect and sizes it against the < 2ms total VFX GPU budget
- Pools explosion VFX instances and triggers them through events, rather than creating a graph per explosion
- Reduces cost for distant or off-screen explosions (particle LOD, bounds-based culling) and scales counts by quality tier
- Avoids any fix that reads GPU particle data back to the CPU
- Does NOT change the gameplay event system — proposes a VFX-side budgeting solution
### Case 5: Context pass — render pipeline (URP or HDRP)
**Input:** Project context: URP render pipeline, Unity 2022.3. Request: "Add depth of field post-processing."
**Input:** Project context: URP render pipeline, targets PC and Nintendo Switch. Request: "Add depth of field post-processing."
**Expected behavior:**
- Uses URP Volume framework: `DepthOfField` Volume Override component
- Uses URP Volume framework: a `DepthOfField` override in a Volume Profile, with script access through `profile.TryGet<>()` as `docs/engine-reference/unity/modules/rendering.md` shows
- Does NOT use HDRP Volume components (e.g., HDRP's `DepthOfField` with different parameter names)
- Notes URP-specific DOF limitations vs HDRP (e.g., Bokeh quality differences)
- Produces C# Volume profile setup code compatible with Unity 2022.3 URP package version
- Places it on the Global Volume for the baseline look or a local Volume for an area, with priority and blend distance set
- Gates the effect per platform and quality tier — reduced or disabled on Switch — and keeps post-processing inside its 1-2ms frame budget
---
@@ -72,8 +68,9 @@ No gate IDs assigned.
- [ ] Redirects gameplay and UI code to appropriate agents
- [ ] Returns structured output (node graph descriptions, HLSL code, CustomPass patterns)
- [ ] Distinguishes between URP and HDRP approaches — never cross-contaminates pipeline-specific APIs
- [ ] Flags geometry shader approaches as URP-incompatible when relevant
- [ ] Uses the RenderGraph API for custom URP passes, not the deprecated `Execute` / CommandBuffer path
- [ ] Produces VFX optimizations that do not change gameplay behavior
- [ ] Asks "May I write this to [filepath]?" naming the file before writing
---
@@ -3,7 +3,6 @@
## Agent Summary
Domain: Unity-specific architecture patterns, MonoBehaviour vs DOTS decisions, and subsystem selection (Addressables, New Input System, UI Toolkit, Cinemachine, etc.).
Does NOT own: language-specific deep dives (delegates to unity-dots-specialist, unity-ui-specialist, etc.).
Model tier: Sonnet (default).
No gate IDs assigned.
---
@@ -11,8 +10,8 @@ No gate IDs assigned.
## Static Assertions (Structural)
- [ ] `description:` field is present and domain-specific (references Unity patterns / MonoBehaviour / subsystem decisions)
- [ ] `allowed-tools:` list includes Read, Write, Edit, Bash, Glob, Grep
- [ ] Model tier is Sonnet (default for specialists)
- [ ] `tools:` list includes Read, Write, Edit, Bash, Glob, Grep
- [ ] Model tier is `inherit` — frontmatter `model:` reads exactly `inherit` (tiers: `.claude/docs/model-tiers.md`)
- [ ] Agent definition acknowledges the sub-specialist routing table (DOTS, UI, Shader, Addressables)
---
@@ -33,51 +32,54 @@ No gate IDs assigned.
**Input:** "Set up a Node scene tree with signals for this enemy system."
**Expected behavior:**
- Does NOT produce Godot Node/signal code
- Identifies this as a Godot pattern
- States that in Unity the equivalent is GameObject hierarchy + UnityEvent or C# events
- Maps the concepts: Godot Node → Unity MonoBehaviour, Godot Signal → C# event / UnityEvent
- Confirms the project is Unity-based before proceeding
- Identifies node trees and signals as Godot concepts
- Confirms the project's engine from `engine.name` in `project.yaml` rather than guessing; if it is not Unity, says it is the wrong specialist for this project instead of translating
- If `engine.name` is unset, says no engine is configured and asks which engine the project uses — it does not assume Unity
- In a Unity project, maps the concepts: node tree → GameObject hierarchy with composed MonoBehaviours; signal → C# event or UnityEvent (consistent with its own rule to use events instead of `SendMessage()` / `Find()`)
- Does not hand the request to a Godot specialist — its Agent grant covers only the four Unity sub-specialists
### Case 3: Unity version API flag
**Input:** "Use the new Unity 6 GPU resident drawer for batch rendering."
**Expected behavior:**
- Identifies the Unity 6 feature (GPU Resident Drawer)
- Flags that this API may not be available in earlier Unity versions
- Asks for or checks the project's Unity version before providing implementation guidance
- Directs to verify against official Unity 6 documentation
- Does NOT assume the project is on Unity 6 without confirmation
- Checks `docs/engine-reference/unity/VERSION.md` and `docs/engine-reference/unity/modules/rendering.md` before answering, not training data (the version file places Unity 6 past the model's knowledge cutoff)
- States what the reference documents: GPU Resident Drawer is a Unity 6+ feature enabled in the URP Asset (Rendering > GPU Resident Drawer), so it requires an SRP project
- Flags the installed-version gap: when `Installed at pin time` in the version file is not determined, asks which editor version is installed before recommending the feature
- Does NOT assume the installed editor is Unity 6 because the reference is pinned to it
- Proposes the URP Asset change and asks before editing it; routes render pipeline customization beyond the setting to `unity-shader-specialist`
### Case 4: DOTS vs. MonoBehaviour conflict
**Input:** "The combat system uses MonoBehaviour for state management, but we want to add a DOTS-based projectile system. Can they coexist?"
**Expected behavior:**
- Recognizes this as a hybrid architecture scenario
- Explains the hybrid approach: MonoBehaviour can interface with DOTS via SystemAPI, IComponentData, and managed components
- Explains the coexistence mechanism at architecture level, as `docs/engine-reference/unity/plugins/dots-entities.md` documents it: authoring MonoBehaviours baked into entities by a `Baker<T>`, projectile data held in `IComponentData`, processed by an `ISystem`
- Notes the performance and complexity trade-offs of mixing the two patterns
- Recommends escalating the architecture decision to `lead-programmer` or `technical-director`
- Defers to `unity-dots-specialist` for the DOTS-side implementation details
### Case 5: Context pass — Unity version
**Input:** Project context provided: Unity 2023.3 LTS. Request: "Configure the new Input System for this project."
**Input:** Project context provided: Unity 6.3 LTS, keyboard/mouse and gamepad targets. Request: "Configure the new Input System for this project."
**Expected behavior:**
- Applies Unity 2023.3 LTS context: uses the New Input System (com.unity.inputsystem) package
- Does NOT produce legacy Input Manager code (`Input.GetKeyDown()`, `Input.GetAxis()`)
- Notes any 2023.3-specific Input System behaviors or package version constraints
- References the project version to confirm Burst/Jobs compatibility if the Input System interacts with DOTS
- Applies the Unity 6.3 LTS context from `docs/engine-reference/unity/modules/input.md`: the Input System package (`com.unity.inputsystem`) with Active Input Handling set to the new system
- Does NOT produce legacy Input Manager code (`Input.GetKeyDown()`, `Input.GetAxis()`) — the engine reference lists these as deprecated
- Defines actions in an `.inputactions` asset, uses the Player Input component or a generated C# class, and prefers action callbacks (`performed`, `canceled`) over polling in `Update()`
- Sets up keyboard+mouse and gamepad control schemes with automatic switching
- Routes UI navigation and input-prompt work to `unity-ui-specialist`
- Treats adding the package as a dependency decision needing `technical-director` sign-off, and asks before writing any settings or asset
---
## Protocol Compliance
- [ ] Stays within declared domain (Unity architecture decisions, pattern selection, subsystem routing)
- [ ] Redirects Godot patterns to appropriate Godot specialists or flags them as wrong-engine
- [ ] Treats Godot patterns as wrong-engine: checks `engine.name`, answers with the Unity equivalent, never emits Godot code
- [ ] Redirects DOTS implementation to unity-dots-specialist
- [ ] Redirects UI implementation to unity-ui-specialist
- [ ] Flags Unity version-gated APIs and requires version confirmation before suggesting them
- [ ] Checks `docs/engine-reference/unity/` (pinned version and `Installed at pin time`) before suggesting a version-gated API, and confirms the installed version when it is not recorded
- [ ] Returns structured pattern decision guides, not freeform opinions
---
## Coverage Notes
- MonoBehaviour vs. ScriptableObject (Case 1) should be documented as an ADR if it results in a project-level decision
- Version flag (Case 3) confirms the agent does not assume the latest Unity version without context
- Version flag (Case 3) confirms the agent checks the pinned and installed Unity version rather than assuming the latest
- DOTS hybrid (Case 4) verifies the agent escalates architecture conflicts rather than resolving them unilaterally
@@ -3,7 +3,6 @@
## Agent Summary
Domain: Unity UI Toolkit (UXML/USS), UGUI (Canvas), data binding, runtime UI performance, and UI input event handling.
Does NOT own: UX flow design (ux-designer), visual art style (art-director).
Model tier: Sonnet (default).
No gate IDs assigned.
---
@@ -11,8 +10,8 @@ No gate IDs assigned.
## Static Assertions (Structural)
- [ ] `description:` field is present and domain-specific (references UI Toolkit / UGUI / Canvas / data binding)
- [ ] `allowed-tools:` list includes Read, Write, Edit, Bash, Glob, Grep
- [ ] Model tier is Sonnet (default for specialists)
- [ ] `tools:` list includes Read, Write, Edit, Bash, Glob, Grep
- [ ] Model tier is `sonnet` — frontmatter `model:` reads exactly `sonnet` (tiers: `.claude/docs/model-tiers.md`)
- [ ] Agent definition does not claim authority over UX flow design or visual art direction
---
@@ -24,7 +23,7 @@ No gate IDs assigned.
**Expected behavior:**
- Produces a UXML document defining the inventory panel structure (ListView, item templates, detail panel)
- Produces USS styles for the inventory layout and item states (default, hover, selected)
- Provides C# code binding the inventory data model to the UI via `INotifyValueChanged` or `IBindable`
- Provides C# code binding the inventory data model to the UI through the runtime binding system: a ViewModel implementing `INotifyBindablePropertyChanged`, with the UI reading through bindings and never writing game state
- Uses `ListView` with `makeItem` / `bindItem` callbacks for the scrollable item list
- Does NOT produce the UX flow design — implements from a provided spec
@@ -39,28 +38,29 @@ No gate IDs assigned.
### Case 3: UI Toolkit data binding for dynamic list
**Input:** "The inventory list needs to update in real time as items are added or removed from the player's bag."
**Expected behavior:**
- Produces the `ListView` pattern with a bound `ObservableList<T>` or event-driven refresh approach
- Uses `ListView.Rebuild()` or `ListView.RefreshItems()` on the backing collection change event
- Notes the performance considerations for large lists (virtualization via `makeItem`/`bindItem` pattern)
- Does NOT use `QuerySelector` loops to update individual elements as a list refresh strategy — flags that as a performance antipattern
- Produces the `ListView` pattern over the inventory's backing collection, virtualized with `makeItem` / `bindItem` so only visible rows exist
- Drives the refresh from the inventory's change event (game state → ViewModel → binding), not from per-frame polling
- Does NOT refresh the list by querying the visual tree for each element — flags per-frame visual-tree queries as an antipattern and caches references instead
- Does NOT create and destroy row elements on each change — reuses rows through the virtualized list
### Case 4: Canvas performance — overdraw
**Input:** "The main menu canvas is causing GPU overdraw warnings; there are many overlapping panels."
**Expected behavior:**
- Identifies overdraw causes: multiple stacked canvases, full-screen overlay panels not culled when inactive
- Recommends:
- Separate canvases for world-space, screen-space-overlay, and screen-space-camera layers
- Disable/deactivate panels instead of setting alpha to 0 (invisible alpha-0 panels still draw)
- Canvas Group + alpha for fade effects, not individual Image alpha
- Notes UI Toolkit alternative if the project is in a migration position
- Confirms the hotspot with a profiler before changing structure (Frame Debugger, Profiler UI module)
- Recommends its documented Canvas remediation:
- One Canvas per logical UI layer, with frequently changing and static content on separate Canvases
- Explicit `Canvas.sortingOrder` rather than hierarchy order
- Shared sprite atlases so panels batch, and Raycast Target disabled on non-interactive elements
- A `CanvasGroup` to fade or hide a group of elements, not per-Image alpha changes
- Notes that screen-space menus are UI Toolkit territory under its own selection rules (the engine reference also marks UGUI Canvas as superseded for new projects), offered as an option rather than a unilateral rewrite
### Case 5: Context pass — Unity version
**Input:** Project context: Unity 2022.3 LTS. Request: "Implement the settings panel with data binding."
**Expected behavior:**
- Uses UI Toolkit with the 2022.3 LTS version of the runtime binding system
- Notes that Unity 2022.3 introduced runtime data binding (as opposed to editor-only binding in earlier versions)
- Does NOT use the Unity 6 enhanced binding API features if they are not available in 2022.3
- Produces code compatible with the stated Unity version, with version-specific API notes
- Checks the stated version against `docs/engine-reference/unity/VERSION.md`, which pins Unity 6.3 LTS, and asks which editor is actually installed before choosing a binding API
- Does NOT state from memory which Unity version introduced runtime data binding — the engine reference does not document it, so the claim is flagged as unverified
- Does NOT emit the runtime binding system (`INotifyBindablePropertyChanged`) for a 2022.3 editor until that API is confirmed available there
- Keeps the version-independent parts of its pattern regardless: UI reads state, user actions dispatch commands, the panel never writes game state directly
---
@@ -69,13 +69,14 @@ No gate IDs assigned.
- [ ] Stays within declared domain (UI Toolkit, UGUI, data binding, UI performance)
- [ ] Redirects UX flow design to ux-designer
- [ ] Returns structured output (UXML, USS, C# binding code)
- [ ] Uses the correct Unity UI framework version for the project's Unity version
- [ ] Flags Canvas overdraw as a performance antipattern and provides specific remediation
- [ ] Does not use alpha-0 as a hide/show pattern — uses SetActive() or VisualElement.style.display
- [ ] Checks the project's Unity version against `docs/engine-reference/unity/VERSION.md` before using version-dependent UI APIs, and asks when the stated version and the pin disagree
- [ ] Flags Canvas overdraw as a performance antipattern and provides specific remediation (Canvas split by layer and update rate, atlasing, Raycast Target off)
- [ ] Hides elements with its documented mechanism — `VisualElement.visible = false` in UI Toolkit, a `CanvasGroup` in UGUI — never by setting each element's alpha to 0
- [ ] Asks "May I write this to [filepath]?" naming the file before writing
---
## Coverage Notes
- Inventory UI (Case 1) should have a manual walkthrough doc in `production/qa/evidence/`
- Inventory UI (Case 1) should have a retained screenshot of each screen touched in `production/qa/evidence/`
- Dynamic list binding (Case 3) should have an integration test or automated interaction test
- Canvas overdraw (Case 4) verifies the agent knows the correct Unity UI performance patterns