English | 日本語
This document covers internal development workflow for unity-cli.
- CLI runtime: Rust (
src/) - Unity bridge package:
UnityCliBridge/Packages/unity-cli-bridge - Unity test project:
UnityCliBridge - C# LSP:
lsp/ - Spec workflow: GitHub Issue-first (
gwt-spec) only
| Tool | Version | Purpose |
|---|---|---|
| Rust toolchain (stable) | latest | CLI build and test |
| .NET SDK | 10.0 | LSP server build and test |
| Unity Editor | 2022.3 LTS or Unity 6 | E2E tests (requires live connection) |
Python + tiktoken |
3.9+ | LSP perf token measurement (scripts/lsp-perf-check.sh) |
# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# .NET SDK 10
# https://dotnet.microsoft.com/download/dotnet/10.0Rust and .NET SDK 10 are included in the development Docker image.
# Build image
docker build -t unity-cli-dev .
# Run all tests (default)
docker run --rm unity-cli-dev
# Run individual tests
docker run --rm unity-cli-dev cargo test
docker run --rm unity-cli-dev dotnet test lsp/Server.Tests.csprojunity-cli works with defaults, but these variables are recommended for CI and multi-instance setups.
| Env | Default | Notes |
|---|---|---|
UNITY_PROJECT_ROOT |
auto-detect | Directory containing Assets/ and Packages/ |
UNITY_CLI_HOST |
127.0.0.1 |
Unity TCP listener host |
UNITY_CLI_PORT |
6400 |
Unity TCP listener port |
UNITY_CLI_TIMEOUT_MS |
30000 |
Command timeout (ms) |
UNITY_CLI_LSP_MODE |
auto |
off, auto, required |
UNITY_CLI_LSP_AUTO_TIMEOUT_MS |
10000 |
auto symbol search wait before index fallback |
UNITY_CLI_UNITYD_IDLE_TIMEOUT |
600 |
Daemon idle timeout (seconds) |
UNITY_CLI_TOOLS_ROOT |
platform default | Root directory for downloaded tools |
Minimal setup:
export UNITY_PROJECT_ROOT=./UnityCliBridge
export UNITY_CLI_HOST=127.0.0.1
export UNITY_CLI_PORT=6400Unity setting path: Edit -> Project Settings -> Unity CLI Bridge
Host: bind/listen hostPort: TCP port (must matchUNITY_CLI_PORT)Apply & Restart: restarts Unity listener
For Docker, WSL2, and multi-instance examples, see
docs/configuration.md.
Legacy MCP-prefixed variables are not supported. Use UNITY_CLI_* only.
UNITY_CLI_UNITYD has been removed; unityd is always auto-managed.
Remote typed, raw and batch commands start unityd on demand when its IPC endpoint
is unavailable, including after the idle timeout (600 seconds by default;
UNITY_CLI_UNITYD_IDLE_TIMEOUT overrides it). Automatic startup uses the invoking
CLI binary and serializes concurrent starts. unityd stop stops the current
process; the next remote operation starts it again. unityd status does not
start it. Host/port resolution is unchanged, and connections are pooled by both.
Before reusing a pooled connection, unityd checks whether the Editor closed it
(Editor restart or bridge reload) and reconnects before sending, so the first call
after a restart does not fail with early eof / Broken pipe. A request that was
already sent is never resent, so mutating calls are not executed twice.
If startup fails, stderr explains the failure and direct TCP fallback.
Use -v to inspect route, startup_ms and operation_ms separately on stderr;
JSON results stay on stdout. unityd status reports the PID and connection count.
Use one of these paths:
- Typed subcommands (common operations)
- Raw tool calls (full coverage)
Typed examples:
unity-cli system pingunity-cli scene create MainSceneunity-cli instances listunity-cli instances set-active --name "<instance>"unity-cli tool listunity-cli tool schema <tool_name> --output jsonunity-cli tool call <tool_name> --json '{...}'unity-cli --dry-run tool call <tool_name> --json '{...}'unity-cli unityd start/stop/statusunity-cli batch --json '[{"tool":"ping","params":{}},{"tool":"get_editor_state","params":{}}]'
Raw example:
unity-cli raw create_gameobject --json '{"name":"Player"}'Parameter validation is strict by default for tools with explicit schemas.
Unknown keys, missing required fields, and type mismatches are rejected before execution.
oneOf / anyOf constraints are also enforced (e.g. load_scene, delete_gameobject, input_keyboard).
For action-based tools, required fields are validated per action variant (e.g. package_manager search requires keyword, manage_layers add requires layerName).
Local (Rust-side) tools that do not require Unity TCP roundtrip:
readsearchlist_packagesget_symbolsbuild_indexupdate_indexfind_symbolfind_refs
Index workflow example:
unity-cli tool call build_index --json '{}'
unity-cli tool call find_symbol --json '{"name":"MyClass","kind":"class","exact":true}'
unity-cli tool call find_refs --json '{"name":"MyClass","pageSize":20}'Tool catalog sources:
- Rust catalog:
src/tooling/tool_catalog.rs - Local tool implementation:
src/tooling/local_tools.rs - Snapshot list:
docs/tools.md(Tool Catalog)
# Rust
cargo fmt
cargo clippy --all-targets -- -D warnings
cargo test --all-targets
cargo llvm-cov --all-targets --summary-only --fail-under-lines 90
# C# LSP
dotnet test lsp/Server.Tests.csproj
dotnet test lsp/Server.Tests.csproj /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:Threshold=90 /p:ThresholdType=line /p:ThresholdStat=total
# Unity (EditMode tests)
./scripts/switch-unity-manifest.sh 6
unity -batchmode -nographics -projectPath UnityCliBridge -runTests -testPlatform editmode -testResults test-results/editmode-unity6.xml
./scripts/switch-unity-manifest.sh 2022
unity -batchmode -nographics -projectPath UnityCliBridge -runTests -testPlatform editmode -testResults test-results/editmode-unity2022.xmlRust coverage gate is enforced at repository level (all Rust targets, line >= 90%).
chmod +x .husky/pre-push
git config core.hooksPath .huskygit push will automatically run cargo test and dotnet test.
- Write failing tests (RED)
- Implement minimum change (GREEN)
- Refactor while tests stay green
Keep test-first commit order whenever possible.
unity-cli test reuses the Editor for --project-path when it is already
running. Otherwise it starts that project's installed Editor in batch mode and
stops only the process it started, including on compilation errors and timeout.
The project must contain the Unity CLI Bridge and Unity Test Framework packages.
Editor selection uses ProjectSettings/ProjectVersion.txt; UNITY_EDITOR_PATH
can select an installed binary explicitly.
unity-cli test --project-path ./UnityCliBridge --mode editmode \
--report junit --output ./artifacts/editmode.xml --timeout 600
unity-cli test --project-path ./UnityCliBridge --mode playmode \
--filter MyPlayModeTests --report nunit --output ./artifacts/playmode.xml
# Optional: exercise Enter Play Mode without Domain Reload
unity-cli test --project-path ./UnityCliBridge --mode playmode \
--disable-domain-reload --output ./artifacts/playmode-no-reload.xmlFor this subcommand, --output is the report filename, relative to the current
directory; other commands retain their --output text|json presentation option.
--filter follows run_tests: a dotted name selects an exact test, otherwise it
selects a fixture. PlayMode preserves the project's Domain Reload configuration
unless --disable-domain-reload is supplied. Reconnection polls the same run ID
and never restarts the tests. NUnit reports use the NUnit 3 XML structure; JUnit
reports include failures, skipped cases, durations and captured test output.
Exit codes are 0 for success, 8 for failed tests, 6 for execution failures
(including compilation errors), and 7 when the Editor cannot be reached.
A test execution timeout after the run starts is 6. The timeout covers startup
and the test run; terminating an owned process can take up to 10 additional
seconds. Failed tests still produce a report before exit 8.
--output-format github emits GitHub error annotations; file and line metadata
are included when Unity's failure stack trace contains a source location.
Example GitHub Actions workflow for an already provisioned self-hosted macOS Apple Silicon runner (this example is not run by this repository's CI):
name: Unity tests
on: [push, pull_request]
permissions:
contents: read
jobs:
editmode:
runs-on: [self-hosted, macOS, ARM64]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- run: cargo build --release --bin unity-cli
- name: Run Unity tests
env:
UNITY_CLI_NO_AUTO_UPDATE: '1'
run: |
target/release/unity-cli test --project-path UnityCliBridge \
--mode editmode --report junit --output artifacts/editmode.xml \
--timeout 600 --output-format github
- name: Upload results
if: always()
uses: actions/upload-artifact@v4
with:
name: unity-test-results
path: artifacts/*.xmlInstall the matching Editor and activate a valid Unity license for the runner's OS account before running the job. This command does not install or activate Unity. Configure license-server access or activation according to your Unity subscription; keep any activation credentials in Actions secrets, not in YAML or artifacts. A license failure is an infrastructure failure, not a failed test. Use an isolated runner/project checkout so concurrent jobs cannot open the same Unity project.
Local acceptance verification (requires xmllint and the Jenkins xUnit
JUnit schema):
python3 scripts/e2e-ci-tests.py --version 6000.3.25f1 \
--output /tmp/ci-tests-unity6 --port 6551 --junit-schema /path/to/junit-10.xsd
python3 scripts/e2e-ci-tests.py --version 2022.3.62f3 \
--output /tmp/ci-tests-unity2022 --port 6552 --junit-schema /path/to/junit-10.xsdUnity E2E is not executed in CI. Use it only for local validation against a running Unity Editor with the TCP listener active.
- Open
UnityCliBridgein Unity Editor. - Ensure the Unity CLI Bridge package is loaded.
- Confirm the listener is active on
UNITY_CLI_HOST/UNITY_CLI_PORT(default127.0.0.1:6400).
# Build
cargo build --release
# Smoke E2E
scripts/e2e-test.sh
# Custom attributes, live recompilation/discovery, CLI/Bridge argument rejection,
# Light creation, exception isolation, and focused EditMode collision tests.
# Each run creates its own project, credentials, port, logs and source hashes.
python3 scripts/e2e-custom-tools.py --version 6000.3.25f1 \
--cli target/debug/unity-cli --output /tmp/custom-tools-6000
python3 scripts/e2e-custom-tools.py --version 2022.3.62f3 \
--cli target/debug/unity-cli --output /tmp/custom-tools-2022
# TCP authentication: isolated real Editor, missing/wrong token rejection,
# eval side effects, CLI/unityd success, POSIX 0600, and focused EditMode tests.
# Repeat for 2022.3.62f3; use a fresh output directory for each run.
python3 scripts/e2e-bridge-auth.py --version 6000.3.25f1 \
--cli target/debug/unity-cli --output /tmp/bridge-auth-6000 --editmode
# Temporary legacy opt-out and next-minor deprecation warning
python3 scripts/e2e-bridge-auth.py --version 6000.3.25f1 \
--cli target/debug/unity-cli --output /tmp/bridge-auth-optout-6000 --opt-out
# AudioMixer hierarchy/exposed Volume and AudioImporter persistence (isolated Editor)
bash scripts/e2e-audio-batch-host.sh --unity-version 2022.3.62f3 --port 6481
bash scripts/e2e-audio-batch-host.sh --unity-version 6000.3.25f1 --port 6482
# Reference fetch regression (live Editor; isolated cache; downloads UnityCsReference)
# Run cargo build first, or set UNITY_CLI_BIN to the binary under test.
scripts/e2e-reference-fetch.sh --port 6400
# Automatic ref resolution + provenance regression (live Editor; separate temporary caches)
# Covers the real project, a 6000.4.12f1 fixture, and explicit public ref 6000.4.
scripts/e2e-reference-resolution.sh --port 6400 --project-root UnityCliBridge
# Player build: relevant EditMode tests, real build/report, polling, failures,
# restart recovery, and headless launch (isolated project copy on macOS)
python3 scripts/e2e-player-build.py --launch --editmode
# unityd reconnect after Editor restart (#384; defaults: 6000.3.25f1, port 6471, 5 cycles)
# Kill -> restart on the same port -> ping -> reads -> create_gameobject; the mutating
# call must succeed first time and create exactly one GameObject.
python3 scripts/e2e-unityd-restart.py --workdir /tmp/unityd-restart
# Deterministic input simulation E2E
scripts/e2e-input-tools.sh
# C# eval E2E against an existing listener (see editor-eval.md)
scripts/e2e-eval.sh --unity-cli "$PWD/target/debug/unity-cli"
# C# eval with an isolated batch host (build the debug CLI first)
scripts/e2e-input-batch-host.sh --suite eval --port 6402 --unity-cli "$PWD/target/debug/unity-cli"
# C# eval latency budget (perf-budgets.json: editor_eval; GUI Editor frontmost, release CLI)
python3 scripts/bench-eval.py --port 6400 --require-frontmost --activate --budget editor_eval
# Timeline editing, persistence and Animator evaluation (real Editor)
cargo build --bin unity-cli
scripts/e2e-timeline-batch-host.sh --port 6474
# Isolated project without com.unity.timeline: compile/start and error contract
scripts/e2e-timeline-batch-host.sh --port 6475 --without-timeline
# Fresh project with / without com.unity.ugui: compile/start and uGUI UI tools
# (run for each Unity version to verify, e.g. 6000.3.25f1 and 2022.3.62f3)
scripts/e2e-ugui-batch-host.sh --unity-version 6000.3.25f1 --port 6476 --without-ugui
scripts/e2e-ugui-batch-host.sh --unity-version 6000.3.25f1 --port 6477
# Test-result counting regression (7 EditMode + 2 PlayMode tests)
# Requires the project's default DisableDomainReload setting and a running listener.
scripts/e2e-test-results.sh
# Input Actions types (both creation routes; requires this checkout's listener)
scripts/e2e-input-actions.sh
# Headless batch-host input simulation E2E
scripts/e2e-input-batch-host.sh
# Real Lighting / legacy NavMesh / NavMeshSurface / Occlusion bake and reload
cargo build --bin unity-cli
scripts/e2e-bake-batch-host.sh --port 6477
# Isolated optional hot reload backend / real Editor checks
scripts/e2e-hot-reload-batch-host.sh --port 6484 --expect missing
# See docs/hot-reload.md for real method replacement runs (ARM64 / x64 under Rosetta).
# PlayMode result collection with Domain Reload enabled and disabled
python3 scripts/e2e-test-domain-reload.py --batch-host --port 6450
# Recommended local path when no Unity GUI listener is already running
scripts/e2e-input-batch-host.sh --port 6402
# Input Actions source JSON persistence, reimport, and Editor restart (two owned batch hosts)
cargo build --bin unity-cli
python3 scripts/e2e-input-actions-persistence.py --port 6428
# The batch-host helper uses the editor version in ProjectVersion.txt by default
# and only needs UNITY_PATH when that editor is not installed locally.
# Full local E2E sweep
scripts/e2e-all-tools.sh
# Full local E2E sweep with custom host/port
scripts/e2e-all-tools.sh --host 192.168.1.10 --port 9090
# Media / Profiler benchmark artifacts under UnityCliBridge/.unity/perf-media/
scripts/perf-media-benchmark.sh
# Recording formats: MP4, WebM and PNG sequence (graphics-enabled Editor in Play mode)
scripts/e2e-video-formats.sh --port 6400The all-tools sweep checks the runtime catalog against its invocations and
scripts/e2e-all-tools-exclusions.json. Each exclusion explains the required
environment or separate suite; excluded tools are reported as skipped, not passed.
cargo test all_tools_e2e -- --test-threads=1 detects catalog drift and verifies
that disconnected or reloading Editors are not treated as ready. For an isolated
Editor project, set UNITY_PROJECT_ROOT=/absolute/project/path and pass its port.
The sweep quits that Editor unless --skip-quit is specified.
Baking E2E creates fixtures under Assets/Scenes/Generated/E2E/Baking/ and
checks generated assets, saved scene references, and navigation queries after
scene reload. Its batch host enables graphics for actual lightmap generation;
do not add -nographics. Logs and pass/fail counts are kept in the printed
/tmp/unity-cli-bake-e2e.* directory. Against a running Editor, use
scripts/e2e-bake.sh --port <port>. Save unrelated scenes first. Use
--targets lighting,navmesh-legacy,navmesh-surface,occlusion to select targets;
acceptance verification requires the default full set.
Timeline E2E uses Assets/Scenes/Generated/E2E/Timeline/ for its fixture
scene and assets. The suite checks clip timing, binding, asset unload/reimport
and scene reload, numeric Animator evaluation, edits/removal, and rejected
operations. The batch helper preserves Editor and test logs in the printed
/tmp/unity-cli-timeline-e2e.* directory. The package-absent variant copies
the Bridge into a temporary project; it never removes the working project's
Timeline dependency. Recorder is also excluded from the isolated project because
it depends on Timeline; the runner asserts neither package is in the resolved
package lock. Set UNITY_PATH or UNITY_CLI to override binaries.
Against an existing listener, run scripts/e2e-timeline.sh --port <port>;
save unrelated dirty scenes first because the fixture opens its own scene.
Run the existing acceptance suites in isolated projects for every supported
Editor. The source UnityCliBridge project is never opened by this runner.
Build the CLI, publish the LSP into a dedicated directory, and select an Editor:
cargo build --bin unity-cli
dotnet publish lsp/Server.csproj -c Release -r osx-arm64 --self-contained true \
-o /tmp/unity-cli-matrix-tools/csharp-lsp/osx-arm64
scripts/e2e-matrix.sh \
--editor /Applications/Unity/Hub/Editor/6000.7.0b2/Unity.app/Contents/MacOS/Unity \
--lsp-root /tmp/unity-cli-matrix-toolsOmit --editor to run all eight versions listed in the
dated compatibility matrix; repeat it to select several.
--port chooses an unused local listener port. --output names a new evidence
directory. --suites compile,input,timeline,vfx performs a focused diagnostic run;
its report explicitly records full_matrix: false. Acceptance uses the default
complete suite set. A missing Editor, compiler error, failed suite or timeout
fails the run. Inspect matrix.json and the linked per-suite logs before claiming PASS.
The default matrix also includes bake, player-build, video-formats,
animation-curves, input-actions-persistence, and reference. To reproduce
only this extension, pass
--suites bake,player-build,video-formats,animation-curves,input-actions-persistence,reference.
The Player suite checks macOS output and, when Windows Build Support is installed,
starts a second isolated Editor for Windows output (port + 2); Input Actions owns another project and two sequential
Editor starts (port + 1) to prove persistence across restart. Install Windows
Build Support in Unity Hub to exercise actual cross-building. Without it, the
runner asserts BUILD_MODULE_MISSING and records UNSUPPORTED, never PASS.
An all-supported run reports PASS; an otherwise successful run containing
explicit unsupported results reports UNSUPPORTED and exits zero. Unexpected
errors still fail. Video capture keeps graphics enabled. Reference suites use
isolated caches and download public UnityCsReference with license acceptance.
Graphics stay enabled for VFX/SDF. Only processes launched by the runner are stopped. Project settings, generated assets and package imports remain in the retained temporary fixture. Editor reloads and scripts may reset the listener; readiness requires an idle compiler, zero compilation errors and the owned port.
Unity 6000.7 Editor tests run on Mono. CoreCLR is a Player backend and is outside this Editor matrix; the compatibility page links the official runtime boundary.
The Bridge uses reflection for the optional com.unity.visualeffectgraph
package; installing the Bridge does not install VFX Graph. The development
project includes VFX Graph 17.4.0. Use Unity 6000.4.11f1 for this matrix.
検証済み: Unity 6000.4.11f1 / VFX Graph 17.4.0 (macOS).
cargo build --bin unity-cli
scripts/e2e-vfx-batch-host.sh --port 6476
scripts/e2e-vfx-batch-host.sh --port 6477 --without-vfxThe normal run requires graphics/compute shader support for SDF baking; do not
add -nographics. It creates a graph from a template, adds a context, block and
parameter, verifies describe/compile, drives a VisualEffect in Play Mode with
parameter writes and events (including OnPlay), and checks a baked Texture3D.
Generated assets belong under Assets/Scenes/Generated/E2E/Vfx/.
The package-absent run uses a temporary project and preserves the working
project's manifest. It verifies compilation and VFX_PACKAGE_MISSING responses
from all six VFX tools without Console errors.
Run the focused NUnit suites on the same integrated source:
UNITY_PATH=/Applications/Unity/Hub/Editor/6000.4.11f1/Unity.app/Contents/MacOS/Unity
"$UNITY_PATH" -batchmode -projectPath "$PWD/UnityCliBridge" -runTests \
-testPlatform EditMode -testFilter UnityCliBridge.Tests.VfxGraphHandlerTests \
-testResults /tmp/vfx-editmode.xml -logFile /tmp/vfx-editmode.log
"$UNITY_PATH" -projectPath "$PWD/UnityCliBridge" -runTests \
-testPlatform PlayMode -testFilter VfxRuntimePlayModeTests \
-testResults /tmp/vfx-playmode.xml -logFile /tmp/vfx-playmode.logThe PlayMode suite needs a visible Editor/Game view to exercise GPU particle
counts and output-event callbacks. Omit -batchmode for that suite: batch mode
can advance the managed test loop without the rendered frames these assertions
need. The command exits automatically when tests finish. EditMode tests and the
CLI E2E host can run in batch mode with graphics enabled.
Keep the XML results, Editor logs and E2E artifact paths in the integration PR. Windows verification is tracked separately in Issue #308.
- Stable tracked scenes stay in
UnityCliBridge/Assets/Scenes/(SampleSceneonly). - Local E2E-generated scenes go under
UnityCliBridge/Assets/Scenes/Generated/E2E/and must not be committed. - UI manual test scenes continue to use
UnityCliBridge/Assets/Scenes/Generated/UI/.
scripts/e2e-video-formats.sh checks actual file headers and session cleanup. It controls Play mode and restores its initial state; use a dedicated local Editor with a camera scene loaded. For a batch host, omit -nographics so Game View can render. Video start returns the planned output path; successful stop returns an existing nonempty file. For png_sequence, this is the first PNG in a unique session directory.
scripts/lsp-perf-check.shis still the canonical benchmark for LSP/search/index performance.scripts/perf-media-benchmark.shis a separate runtime benchmark for screenshot/video/profiler flows.scripts/perf-media-benchmark.shgeneratesAssets/Scenes/Generated/E2E/Performance/UnityCli_PerfBenchmark.unityviaTools/Unity CLI/Performance/Generate Media Perf Scene.- Benchmark outputs are written under
UnityCliBridge/.unity/perf-media/<timestamp>/. - The script captures:
get_command_statsprofiler_start/profiler_stopcapture_screenshotcapture_video_start/capture_video_status/capture_video_stop
- Review
summary.mdandresult.jsonin the artifact directory for CLI latency, bridge timing deltas, and profiler metrics.
- Unity Editor is running.
Unity CLI Bridgepackage is installed.- Unity TCP listener is active (default
6400). UNITY_CLI_HOST/UNITY_CLI_PORTpoints to that listener.
| Symptom | Cause | Fix |
|---|---|---|
Connection timeout |
Unity not running | Start Unity Editor |
ECONNREFUSED |
Listener not active / wrong port | Reopen Unity project settings and restart listener |
invalid response |
Protocol mismatch or stale build | Reimport package and restart Unity |
| Symptom | Fix |
|---|---|
| LSP command not found | Run unity-cli lsp install and retry |
| LSP timeout | Increase UNITY_CLI_TIMEOUT_MS and retry |
| LSP required but unavailable | Use UNITY_CLI_LSP_MODE=auto during setup |
Symbol search returns backend: "index" |
Read fallbackReason; run unity-cli lsp install or raise UNITY_CLI_LSP_AUTO_TIMEOUT_MS |
export UNITY_CLI_HOST=host.docker.internal
export UNITY_CLI_PORT=6400
export UNITY_PROJECT_ROOT=/absolute/path/to/UnityCliBridgeThe CLI is the primary implementation. For MCP clients, use the thin
unity-cli mcp stdio adapter and unity-cli mcp configure <client>;
see MCP configuration. Legacy Node.js MCP launch settings are not supported.
Verification:
unity-cli --output json system ping
echo "$UNITY_CLI_HOST:$UNITY_CLI_PORT"CI is defined in .github/workflows/lint.yml, .github/workflows/test.yml, .github/workflows/published-install.yml, and .github/workflows/skill-routing-eval.yml.
| Job | Trigger | Description |
|---|---|---|
| Skill Contract Lint (required) | push / PR | cargo run -- skills lint --severity error (Skill Contract v1; SPEC #160) |
| Rust Tests (required) | push / PR | cargo test |
| LSP Tests (required) | push / PR | dotnet test lsp/Server.Tests.csproj |
| LSP Performance (required) | push / PR | scripts/lsp-perf-check.sh (full cases + history artifact) |
| CLI Latency (required) | push / PR | scripts/bench-cli-latency.py (p50/p95 budgets + injected-delay evidence) |
| Skill Routing Eval | daily schedule / manual | scripts/skill-eval/llm-routing-eval.sh (.github/workflows/skill-routing-eval.yml) |
| Windows Published Install | release / cron / manual | latest release via install.ps1 (.github/workflows/published-install.yml) |
Skill Contract Check, Rust Tests, LSP Tests, LSP Performance, and CLI Latency are required checks for PR merges.
The full current capability list (typed command groups + Unity Tool APIs) is maintained in docs/tools.md under:
Tool Catalog
Regenerate command examples:
unity-cli --help
unity-cli tool list --host 127.0.0.1 --port 6400 --output json | jq -r '.data[].name'perf-budgets.json is the source of p50/p95 latency limits. A missing budget,
failed operation, absolute limit violation, or relative regression fails the run.
Percentiles use the nearest rank; failed operations never count as fast samples.
| Gate | Environment | Samples per operation | Evidence |
|---|---|---|---|
| CLI Latency (required) | Linux CI, deterministic loopback TCP Bridge | 50 after 3 warmups | cli-latency artifact: normal and injected-delay JSON |
Editor perf suite |
macOS GUI, 6000.3.25f1 and 2022.3.62f3 | 30 after 3 complete warmup cycles, each focus condition | matrix.json, perf.json, perf.log |
editor_eval |
macOS GUI, Editor frontmost | 100 after 3 warmups | bench-eval.py --budget editor_eval JSON |
CI measures startup, tool list, and ping/state/batch-of-five over both direct TCP and a warm unityd connection. Direct mode exercises the existing daemon-unavailable fallback using a private unusable runtime path; connection counts prove the route. The job also injects 30 ms into every Bridge response and requires a measured budget failure. No Unity installation is needed.
The real Editor suite measures the staff report's 23 operations (Issue #371 AC-6):
state, hierarchy, object search, Transform, position, Cube creation, component
add/remove, object deletion, scene info/save, console, screenshot, material search,
asset copy/move/delete, import settings, material edit, Time settings, C# read,
Play-until-ready and Stop-until-ready. The 22 remote operations use warm unityd; C# read
uses the current CLI's local reader and is explicitly labelled local in JSON.
Play/Stop include polling until the requested state is observed.
Sampling: each operation still launches a CLI process, so reported wall time includes process spawn and exit. The Editor and unityd stay resident and warm; their initial startup, fixture creation and focus changes are outside the timed interval. This is not a persistent CLI shell measurement. Do not compare it with a resident-shell benchmark without stating that difference.
Focus: frontmost and background have separate budgets. macOS frontmost PID
is checked before and after every sample; background runs activate Finder.
The owned Game View windows use PlayUnfocused during both conditions so entering
Play Mode does not bring a background Editor forward. Their previous settings are
restored afterwards; this window setting is also recorded in the history conditions.
The background screenshot starts with Finder frontmost and may activate the target
Editor: the existing capture handler calls GameView.Focus(). This exception is
recorded as backgroundScreenshot in JSON conditions. Its post-call PID must remain
Finder or become the target Editor; third-party activation is never accepted.
Other focus changes discard the entire balanced cycle and retry; ten discarded cycles
or an OS screenshot fallback fail the run. Only valid cycles supply samples. Use an idle host
without another agent changing Editor focus or importing another project.
Initial Editor budgets come from the four complete runs in
issue-394-baseline.json. For each operation
and focus condition, take the larger measurement across the two Unity versions,
multiply p50 by 1.5 and p95 by 2, and round up to 5 ms. For background p50 only,
use the calibration p95 as a floor before rounding. Background update scheduling
can move request timing between fast and slow phases: the same Unity 6 binary's
material-search p50 changed from 74.6 to 154.3 ms while p95 stayed at 182.0/180.5 ms.
The original failing verification is preserved; the baseline JSON records the
observations and calibration rationale. Frontmost and all p95 budgets are unchanged.
This leaves initial host variance headroom while the separate history gate still rejects p50 degradation
greater than 20%. Existing editor_eval and mock CLI budgets are unchanged.
cargo build --release --bin unity-cli
# Unity-free CI gate (50 samples per operation/route)
python3 scripts/bench-cli-latency.py --out /tmp/cli-latency.json
# Must exit 1 with latency violations and all eight results present
python3 scripts/bench-cli-latency.py --delay-ms 30 --out /tmp/cli-latency-delayed.json
# Release prerequisite: both Editors, frontmost AND background, isolated GUI projects
UNITY_CLI_PERF_REGRESSION_PERCENT=20 python3 scripts/e2e-matrix.py --suites perf \
--unity-cli "$PWD/target/release/unity-cli" \
--editor /Applications/Unity/Hub/Editor/6000.3.25f1/Unity.app/Contents/MacOS/Unity \
--editor /Applications/Unity/Hub/Editor/2022.3.62f3/Unity.app/Contents/MacOS/Unity \
--output /tmp/unity-cli-release-perf
# Repeat against an already-open project owned by the perf matrix
python3 scripts/bench-editor-ops.py --project /tmp/perf-fixture/project \
--port 6508 --focus both --out /tmp/editor-ops.json
# Existing eval and LSP gates
python3 scripts/bench-eval.py --port 6400 --require-frontmost --activate --budget editor_eval
./scripts/lsp-perf-check.shChoose a fresh --output directory for every matrix run. The perf suite uses
GUI Editors; its isolated-project ownership marker prevents the standalone
benchmark from replacing a user's scene. Fixtures live only under
Assets/Scenes/Generated/E2E/Performance/. The LSP binary is not needed for
--suites perf.
The copied URP material-upgrade cache is regenerated for the selected Editor;
render pipeline settings are retained while avoiding a cross-version startup dialog.
The suite also runs the existing editor_eval gate for 100 frontmost samples,
saves perf-eval.json/perf-eval.log, and appends its results to the same history
under separate eval conditions. Eval uses its existing direct TCP route.
scripts/bench-editor-ops.py appends all results to
.unity/perf/editor-ops-history.jsonl. Compare p50 against the median of the
last five complete measurement runs with the same host, OS, architecture, Unity version,
focus, Play Mode options, Game View focus mode and sampling method. Fail when degradation is strictly
greater than 20%. Fewer than five matching runs means absolute budgets still
apply and the relative baseline is not yet established. Completed measurements
count even when a budget failed; incomplete/error runs remain in the log but do
not supply a baseline. Absolute budgets always apply alongside the rolling median.
Two-phase conditions: in the 6000.3.25f1 background condition the same binary
reports a remote operation's p50 near 75 ms in one run and near 150 ms in the next,
because background update scheduling answers the call in a fast or a slow phase.
A median over both phases fails every slow-phase run once the fast phase holds the
median (Issue #461). two_phase_p50 in perf-budgets.json therefore declares this
condition and its 20 single-request remote operations. For a declared operation the gate:
- sorts the p50 values of all matching complete runs and splits them at the largest relative gap between neighbours;
- assigns the current p50 to the phase on its side of that gap's geometric midpoint;
- compares it with the median of the last five runs of that phase and fails above the same +20%.
A phase with fewer than five runs has no relative baseline yet, exactly like a condition
with fewer than five matching runs; absolute budgets still apply. A gap no wider than the
threshold is a single phase and keeps the plain last-five median, so an operation that
has only shown one phase still fails on its first slow value. The threshold, the five-run
baseline, the sample counts and all absolute budgets are unchanged. C# read (local), Play/Stop (polled across many
updates), 2022.3.62f3 background and both frontmost conditions are not declared; their
judgments are identical to the plain rule.
A uniform 30% slowdown of either established phase fails. Three limits remain, and the
absolute budgets bound them: a fast-phase run that slows past the midpoint (about +35%
with 75/150 ms phases) reads as the slow phase; after a never-seen slow value fails, the
next four values of that new phase are not compared, so investigate that first failure
instead of re-running; operations whose samples mix both phases inside one run
(material_search, asset_delete, console) report p50 values between the phases and
can still fail while their history is short. Replaying the Issue #397 history
(tests/fixtures/perf/editor-ops-history-issue-397.jsonl), the plain rule failed all 9
gated runs of this condition; the two-phase rule passes 6 and fails 3, all on those mixed
operations. Repeat such a failure on an idle host. Change the declaration only with
history showing both phases for an unchanged binary.
UNITY_CLI_PERF_REGRESSION_PERCENT (or --regression-percent) overrides the
relative threshold; it never disables absolute budgets. Diagnose a failure from
violations, sample counts and condition metadata, then repeat on an idle host.
Fix actual regressions. If a deliberate change justifies new limits, review the
fresh measurements and update only the affected keys in perf-budgets.json.
For an intentional environment/baseline reset, preserve the old JSONL and select
a new --history path; do not delete failing history or raise limits merely to
get a green run. Release verification uses the normal history and default 20%.
/release requires both versions and both focus conditions to PASS before the
release is prepared. Keep the command, version, pass/fail counts and log summary
in the PR. Re-run after the last product or benchmark code change; a historical
PASS is not evidence for changed code. scripts/benchmark.sh and
scripts/perf-media-benchmark.sh remain exploratory tools; they do not replace
the gates above. LSP history remains separate at .unity/perf/lsp-history.jsonl.
Benchmark and history files:
tests/fixtures/skill-routing/benchmark.jsonl(routing benchmark: 127 cases).unity/skill-eval/skill-routing-history.jsonl(append-only eval history).unity/skill-eval/skill-static-report.json(latest static contract report)
Run static validation (required in PR CI):
cargo run -- skills lint --severity errorRun routing eval with predictions:
./scripts/skill-eval/llm-routing-eval.sh \
--model local-debug \
--predictions /path/to/predictions.jsonlRun routing eval with an external runner command:
./scripts/skill-eval/llm-routing-eval.sh \
--model nightly \
--runner-cmd '<your-runner-command>'Run a local Codex-based routing check:
./scripts/skill-eval/llm-routing-eval.sh \
--model codex-local \
--runner-cmd 'python3 scripts/skill-eval/run-codex-routing.py'This local runner requires codex login and unity-cli on PATH. It reads
unity-cli tool schema --output json locally to supplement skill examples
with every available tool and its top-level payload keys; no Editor is needed.
For checkout-specific evaluation, build with cargo build and append
--unity-cli ./target/debug/unity-cli to the runner command. Schema discovery
errors fail the prediction rather than silently using an incomplete catalog.
Current thresholds:
top1 >= 0.90top2 >= 0.98tool_correct >= 0.92payload_valid >= 0.95
- Run
./scripts/publish.sh <major|minor|patch> - Confirm the new
vX.Y.Ztag was pushed - Verify
.github/workflows/release.ymluploaded the release binaries - Verify the crate is available on crates.io
Detailed steps: RELEASE.md.
Periodically verify that docs and issue-first workflow references match the current implementation.
| Directory / File | Contents |
|---|---|
docs/architecture.md |
Architecture overview |
docs/migration-notes.md |
Migration and deprecation notes |
docs/ |
Development guide and constitution |
README.md |
Project overview |
UnityCliBridge/Packages/unity-cli-bridge/README.md |
UPM package docs (EN) |
UnityCliBridge/Packages/unity-cli-bridge/README.ja.md |
UPM package docs (JA) |
-
Legacy name residuals: Search for unintentional
MCPor old project name references.grep -rni "mcp" docs/ README.md | grep -v migration-notes.md
docs/migration-notes.mdintentionally contains old names for migration/deprecation documentation.
-
Environment variable consistency: Compare the variables listed in this document with
src/core/config.rs.grep -oE 'UNITY_CLI_[A-Z_]+' src/core/config.rs | sort -u grep -oE 'UNITY_CLI_[A-Z_]+' docs/development.md | sort -u
-
Source file structure: Verify
docs/architecture.mdfile list matches actual sources.ls src/*.rs -
Command list: Check
README.mdCommand Overview againstsrc/cli.rssubcommand definitions.
- Before merging PRs that add features or change design
- After changes to environment variables or command structure
- As a release checklist item
The Unity-side codebase uses unity-mcp-server as its base copy. Differences are limited to changes required for the MCP → CLI migration. For migration policy and diff notes, see docs/migration-notes.md.
このドキュメントは unity-cli の内部開発フローをまとめたものです。
- CLI本体: Rust (
src/) - Unity連携パッケージ:
UnityCliBridge/Packages/unity-cli-bridge - Unityテストプロジェクト:
UnityCliBridge - C# LSP:
lsp/ - Specワークフロー: GitHub Issue-first(
gwt-spec)のみ
| ツール | バージョン | 用途 |
|---|---|---|
| Rust toolchain (stable) | latest | CLI 本体のビルド・テスト |
| .NET SDK | 10.0 | LSP サーバーのビルド・テスト |
| Unity Editor | 2022.3 LTS または Unity 6 | ローカル Unity listener / ローカル E2E 検証 |
Python + tiktoken |
3.9+ | LSP 性能計測時のトークン算出(scripts/lsp-perf-check.sh) |
# Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# .NET SDK 10
# https://dotnet.microsoft.com/download/dotnet/10.0 からダウンロードRust と .NET SDK 10 が同梱された開発用 Docker イメージを利用できます。
# イメージをビルド
docker build -t unity-cli-dev .
# 全テストを実行 (デフォルト)
docker run --rm unity-cli-dev
# 個別テスト
docker run --rm unity-cli-dev cargo test
docker run --rm unity-cli-dev dotnet test lsp/Server.Tests.csprojunity-cli はデフォルトでも動作しますが、CI や複数インスタンス運用では以下の環境変数の利用を推奨します。
| 環境変数 | デフォルト | 補足 |
|---|---|---|
UNITY_PROJECT_ROOT |
自動検出 | Assets/ と Packages/ を含むディレクトリ |
UNITY_CLI_HOST |
127.0.0.1 |
Unity TCP リスナーのホスト |
UNITY_CLI_PORT |
6400 |
Unity TCP リスナーのポート |
UNITY_CLI_TIMEOUT_MS |
30000 |
コマンドタイムアウト (ms) |
UNITY_CLI_LSP_MODE |
auto |
off, auto, required |
UNITY_CLI_LSP_AUTO_TIMEOUT_MS |
10000 |
auto のシンボル検索が LSP を待つ上限 (ms) |
UNITY_CLI_UNITYD_IDLE_TIMEOUT |
600 |
デーモンアイドルタイムアウト(秒) |
UNITY_CLI_TOOLS_ROOT |
OS依存既定 | ツール配置ルート |
最小設定:
export UNITY_PROJECT_ROOT=./UnityCliBridge
export UNITY_CLI_HOST=127.0.0.1
export UNITY_CLI_PORT=6400Unity 側設定: Edit -> Project Settings -> Unity CLI Bridge
Host: 待受ホストPort: TCP ポート(UNITY_CLI_PORTと一致させる)Apply & Restart: Unity 側リスナー再起動
Docker、WSL2、複数インスタンスの設定例は
docs/configuration.md を参照してください。
旧 MCP プレフィックス環境変数は未サポートです。UNITY_CLI_* のみ使用してください。
UNITY_CLI_UNITYD は廃止済みで、unityd は常時自動管理です。
リモートの typed / raw / batch 操作は、IPC 接続先がない場合に unityd を自動起動します。
idle 終了(既定 600 秒、UNITY_CLI_UNITYD_IDLE_TIMEOUT で変更可能)後も同様です。
自動起動には実行中の CLI と同じバイナリを使い、同時起動を排他制御します。
unityd stop 後の次のリモート操作では再起動し、unityd status 単独では起動しません。
host/port の解決方法は維持し、接続を両方の組み合わせごとに再利用します。
再利用の前に Editor 側で接続が閉じられていないか(Editor の再起動や bridge の再読み込み)を確認し、
閉じられていれば送信前に接続し直します。そのため再起動直後の最初の呼び出しが
early eof / Broken pipe で失敗しません。送信済みの要求は再送しないため、
変更系の呼び出しが二重に実行されることはありません。
起動失敗の理由と直接 TCP 接続への fallback は stderr に表示します。
-v で route / startup_ms / operation_ms を stderr に分けて記録し、
stdout は JSON 結果用に維持します。PID と接続数は unityd status で確認できます。
呼び出し経路は2種類です。
- typed サブコマンド(主要操作)
- raw 呼び出し(全コマンドカバー)
typed 例:
unity-cli system pingunity-cli scene create MainSceneunity-cli instances listunity-cli instances set-active --name "<instance>"unity-cli tool listunity-cli tool schema <tool_name> --output jsonunity-cli tool call <tool_name> --json '{...}'unity-cli --dry-run tool call <tool_name> --json '{...}'unity-cli unityd start/stop/statusunity-cli batch --json '[{"tool":"ping","params":{}},{"tool":"get_editor_state","params":{}}]'
raw 例:
unity-cli raw create_gameobject --json '{"name":"Player"}'明示スキーマを持つツールはデフォルトで厳格バリデーションされます。
未知キー、必須不足、型不一致は実行前にエラーになります。
oneOf / anyOf 制約(例: load_scene, delete_gameobject, input_keyboard)も実行前に検証されます。
action 付きツールは action ごとの必須項目も実行前に検証されます(例: package_manager の search では keyword 必須、manage_layers の add では layerName 必須)。
Unity TCP を介さずローカル実行される Rust 側ツール:
readsearchlist_packagesget_symbolsbuild_indexupdate_indexfind_symbolfind_refs
インデックス運用例:
unity-cli tool call build_index --json '{}'
unity-cli tool call find_symbol --json '{"name":"MyClass","kind":"class","exact":true}'
unity-cli tool call find_refs --json '{"name":"MyClass","pageSize":20}'参照先:
- Rustツールカタログ:
src/tooling/tool_catalog.rs - ローカルツール実装:
src/tooling/local_tools.rs - スナップショット一覧:
docs/tools.md(Tool Catalog)
# Rust
cargo fmt
cargo clippy --all-targets -- -D warnings
cargo test --all-targets
# C# LSP
dotnet test lsp/Server.Tests.csproj
# Unity(EditModeテスト)
./scripts/switch-unity-manifest.sh 6
unity -batchmode -nographics -projectPath UnityCliBridge -runTests -testPlatform editmode -testResults test-results/editmode-unity6.xml
./scripts/switch-unity-manifest.sh 2022
unity -batchmode -nographics -projectPath UnityCliBridge -runTests -testPlatform editmode -testResults test-results/editmode-unity2022.xmlchmod +x .husky/pre-push
git config core.hooksPath .huskygit push 時に自動で cargo test と dotnet test が実行されます。
- 失敗するテストを先に作成(RED)
- 最小実装で通す(GREEN)
- テストを維持したまま整理(REFACTOR)
Unity E2E は CI では実行しません。Unity Editor が起動しているローカル環境でのみ実行します。
VFX Graph の通常・非導入 E2E と EditMode / PlayMode の実行手順は VFX Graph verification を参照してください。
Input Actions のソース JSON 保存・再import・Editor 再起動の回帰検証は、
cargo build --bin unity-cli の後に
python3 scripts/e2e-input-actions-persistence.py --port 6428 を実行します。
専用 batch host を2回起動し、生成アセットを終了時に削除します。
Unity ログ・CLI 応答・集計 summary.json は出力された一時ディレクトリに残ります。
UnityCliBridgeプロジェクトを Unity Editor で開く- Unity CLI Bridge パッケージが読み込まれていることを確認する
UNITY_CLI_HOST/UNITY_CLI_PORTの listener が起動していることを確認する
# ビルド
cargo build --release
# スモークE2E
scripts/e2e-test.sh
# 参照ソース取得の回帰検証(起動済み Editor・隔離キャッシュ・UnityCsReference を取得)
# 先に cargo build、または UNITY_CLI_BIN で検証バイナリを指定する。
scripts/e2e-reference-fetch.sh --port 6400
# 参照の自動解決・取得元情報の検証(起動済み Editor・ケース別の一時キャッシュ)
# 実プロジェクト、6000.4.12f1 fixture、公開 ref 6000.4 の明示指定を検証する。
scripts/e2e-reference-resolution.sh --port 6400 --project-root UnityCliBridge
# Player build: 隔離コピーで EditMode・実ビルド・状態取得・失敗・再起動・画面なし起動
python3 scripts/e2e-player-build.py --launch --editmode
# 入力シミュレーション決定的 E2E
scripts/e2e-input-tools.sh
# テスト件数の回帰検証(EditMode 7件 + PlayMode 2件)
# プロジェクト既定の DisableDomainReload 設定と起動済み listener が必要です。
scripts/e2e-test-results.sh
# Input Actions の型指定(両作成経路、この checkout の listener が必要)
scripts/e2e-input-actions.sh
# headless batch host 入力 E2E
scripts/e2e-input-batch-host.sh
# unityd 自動起動・idle 復帰・同時操作 E2E(専用 batch host を起動・終了)
cargo build
python3 scripts/e2e-unityd.py --port 6453
# 結果・cold/warm 時間・Editor log: UnityCliBridge/.unity/unityd-<timestamp>/
# Editor 再起動後の unityd 再接続 E2E(#384、既定 6000.3.25f1・port 6471・5 サイクル)
# Editor を終了 → 同じポートで再起動 → ping → 読み取り → create_gameobject を繰り返し、
# 変更系が 1 回で成功し、GameObject が 1 つだけできることを確認する
cargo build --release
python3 scripts/e2e-unityd-restart.py --workdir /tmp/unityd-restart
# 結果: <workdir>/run-<timestamp>/result.json(--workdir 指定時は Library を再利用)
# Domain Reload 有効/無効で PlayMode の完了・結果・export・設定復元を検証
python3 scripts/e2e-test-domain-reload.py --batch-host --port 6450
# Unity GUI listener が無い場合の推奨経路
scripts/e2e-input-batch-host.sh --port 6402
# com.unity.ugui 有無の新規プロジェクトでコンパイル・起動・uGUI UI ツールを検証
# (検証対象の Unity バージョンごとに実行。例: 6000.3.25f1 と 2022.3.62f3)
scripts/e2e-ugui-batch-host.sh --unity-version 2022.3.62f3 --port 6476 --without-ugui
scripts/e2e-ugui-batch-host.sh --unity-version 2022.3.62f3 --port 6477
# 既定では ProjectVersion.txt の Unity を使う。
# その editor が未インストールのときだけ UNITY_PATH を上書きする。
# フルローカルE2E
scripts/e2e-all-tools.sh
# ホスト・ポート指定
scripts/e2e-all-tools.sh --host 192.168.1.10 --port 9090
# media capture / profiler ベンチマーク
scripts/perf-media-benchmark.sh
# 録画形式: MP4 / WebM / PNG連番(描画可能なEditor)
scripts/e2e-video-formats.sh --port 6400全ツール E2E の除外理由は scripts/e2e-all-tools-exclusions.json で管理し、
除外ツールを PASS として数えません。cargo test all_tools_e2e -- --test-threads=1
でカタログとの差分と、接続失敗を準備完了と扱わないことを検証します。
隔離した Editor を使う場合は UNITY_PROJECT_ROOT=/absolute/project/path と
ポートを指定してください。--skip-quit を指定しない場合、その Editor を終了します。
入力の tap / swipe は開始受付後すぐ応答し、自然な Input System 更新で
複数フレームにわたって実行します。holdSeconds / duration は実時間で計測し、
swipe は途中の位置と終点をゲームの Update に公開してから解除します。
同じ touchId は受付順に実行し、異なる ID は並行して実行します。
CLI の touchId はゼロ始まりで、Input System の touchId はその値 + 1 です。
touches 配列内の物理スロットは Unity が割り当てます。
batch / sequence の応答もジェスチャー終了待ちではなく受付結果です。
TouchGesturePlayModeTests は実 Touchscreen を MonoBehaviour.Update から観測し、
30fps・120fps・無制限での保持と移動、既定 tap、同一/別 ID、デバイス削除を検証します。
実 Editor の PlayMode テストランナーでこのクラスを指定して実行してください。
UnityCliBridge/Assets/Scenes/直下は固定で追跡するシーン(SampleSceneのみ)を配置する- ローカル E2E 生成シーンは
UnityCliBridge/Assets/Scenes/Generated/E2E/に作成し、コミットしない - UI 手動検証シーン(UGUI/UITK/IMGUI)は
Tools/Unity CLI/UI Tests/*で必要時にUnityCliBridge/Assets/Scenes/Generated/UI/へ生成する
scripts/e2e-video-formats.sh は実ファイルのヘッダーとセッション終了を検証します。Play状態を切り替えて元に戻すため、カメラのあるシーンを開いた専用Editorで実行します。batch-host では Game View を描画できるよう -nographics を外してください。録画開始時は予定パス、停止成功時は存在する空でないファイルのパスを返します。png_sequence の停止結果はセッション固有ディレクトリ内の最初のPNGです。
scripts/lsp-perf-check.shは引き続き LSP / search / index 性能の正規ベンチマークで、scripts/perf-media-benchmark.shとは別物。scripts/perf-media-benchmark.shはTools/Unity CLI/Performance/Generate Media Perf Scene経由でAssets/Scenes/Generated/E2E/Performance/UnityCli_PerfBenchmark.unityを生成する。- 出力 artifact は
UnityCliBridge/.unity/perf-media/<timestamp>/に保存する。 - 保存対象は
get_command_stats、profiler_start/profiler_stop、capture_screenshot、capture_video_start/capture_video_status/capture_video_stop。 - 確認対象は
summary.mdとresult.json。LSP の履歴ファイル.unity/perf/lsp-history.jsonlには追記しない。
- Unity Editor が起動していること
Unity CLI Bridgeパッケージが導入されていること- Unity TCP リスナーが起動していること(デフォルト
6400) UNITY_CLI_HOST/UNITY_CLI_PORTが一致していること
| 症状 | 原因 | 対処 |
|---|---|---|
Connection timeout |
Unity未起動 | Unity Editorを起動 |
ECONNREFUSED |
リスナー未起動 / ポート不一致 | Project Settingsで再起動 |
invalid response |
プロトコル不一致 / 古いビルド | パッケージ再import後にUnity再起動 |
| 症状 | 対処 |
|---|---|
| LSP実行ファイルが見つからない | unity-cli lsp install を実行して再試行 |
| LSPタイムアウト | UNITY_CLI_TIMEOUT_MS を延長 |
| 必須LSPモードで失敗 | セットアップ中は UNITY_CLI_LSP_MODE=auto を利用 |
シンボル検索が backend: "index" を返す |
fallbackReason を確認し、unity-cli lsp install を実行するか UNITY_CLI_LSP_AUTO_TIMEOUT_MS を延長 |
export UNITY_CLI_HOST=host.docker.internal
export UNITY_CLI_PORT=6400
export UNITY_PROJECT_ROOT=/absolute/path/to/UnityCliBridgeCLI が本体です。MCP クライアントには薄い stdio アダプタ unity-cli mcp と
unity-cli mcp configure <client> を使用してください(MCP 設定)。
旧 Node.js MCP サーバーの起動設定はサポートしません。
確認:
unity-cli --output json system ping
echo "$UNITY_CLI_HOST:$UNITY_CLI_PORT"CI は .github/workflows/lint.yml / .github/workflows/test.yml / .github/workflows/published-install.yml / .github/workflows/skill-routing-eval.yml で定義されています。
| ジョブ | トリガー | 内容 |
|---|---|---|
| Skill Contract Lint (required) | push / PR | cargo run -- skills lint --severity error (Skill Contract v1; SPEC #160) |
| Rust Tests (required) | push / PR | cargo test |
| LSP Tests (required) | push / PR | dotnet test lsp/Server.Tests.csproj |
| LSP Performance (required) | push / PR | scripts/lsp-perf-check.sh(全ケース実行 + 履歴artifact) |
| CLI Latency (required) | push / PR | scripts/bench-cli-latency.py(p50/p95 予算 + 遅延注入の証跡) |
| Skill Routing Eval | 毎日スケジュール / 手動 | scripts/skill-eval/llm-routing-eval.sh(.github/workflows/skill-routing-eval.yml) |
| Windows Published Install | Release 後/週次/手動 | 最新の公開版を install.ps1 で導入(.github/workflows/published-install.yml) |
Skill Contract Check / Rust Tests / LSP Tests / LSP Performance / CLI Latency は PR マージの必須チェックです。
最新の全機能一覧(typed コマンド群 + Unity Tool API 一覧)は docs/tools.md の以下を正本とします。
Tool Catalog
再生成コマンド例:
unity-cli --help
unity-cli tool list --host 127.0.0.1 --port 6400 --output json | jq -r '.data[].name'perf-budgets.json の p50/p95 を予算として検査し、超過・予算欠落・操作失敗は
非ゼロで終了します。パーセンタイルは nearest-rank です。失敗を高速なサンプルとして扱いません。
- CI の CLI Latency (required) は Unity 不要のモック TCP Bridge に対して、 起動、tool list、ping、Editor 状態、batch 5 件を各 50 回(warmup 3 回除外)計測します。 リモート操作は直接 TCP と warm unityd の両方を測り、接続数で経路も確認します。 応答に 30 ms の遅延を加えて失敗を確認した JSON も artifact に保存します。
- 実機の perf スイートはスタッフ調査の 23 操作(#371 AC-6)を各 30 回、 warmup 3 サイクル後に測ります。6000.3.25f1 / 2022.3.62f3 の GUI Editor を 最前面・背景の両条件で動かし、条件別予算を適用します。
- Editor と unityd は常駐・warm ですが、CLI は操作ごとに起動します。
計測はプロセス起動から終了までで、Play/Stop は状態確認までを含みます。
常駐 CLI shell の計測とは区別してください。22 操作は warm unityd を使い、C# ファイル読取だけは現行 CLI の
ローカル処理であり、JSON の経路に
localと明記します。 - 最前面 PID を各サンプルの前後で確認し、背景条件は Finder を前面にします。
専用 Game View は両条件とも
PlayUnfocusedにし、Play 開始時の自動前面化を防ぎます。 終了時に元のウィンドウ設定へ戻し、この設定も履歴の比較条件に含めます。 背景スクリーンショットだけは、既存撮影処理のGameView.Focus()により対象 Editor が前面化する 動作を含めます。開始時は Finder が前面、終了時は同じ Finder または対象 Editor に限定し、 第三者 PID への移動は許可しません。この例外を JSON 条件のbackgroundScreenshotに記録します。 フォーカス逸脱時は周全体を破棄して再計測し、有効な周だけを採用します。 10 周の破棄、OS スクリーンショットへのフォールバックは失敗です。 他 Agent の GUI 操作や重い import と同時に計測しません。
Editor の初期予算は 4 条件の実測 に基づきます。
操作・フォーカスごとに 2 Unity バージョンの大きい値を取り、p50 は 1.5 倍、p95 は 2 倍して
5ms 単位に切り上げます。背景の p50 のみ、切り上げ前に校正時の p95 を下限とします。
背景 Editor の更新待ちによる位相差で、同一 Unity6 バイナリの material search は p50 が
74.6→154.3ms に動く一方、p95 は 182.0→180.5ms と安定していました。元の FAIL 記録を保持し、
ベースライン JSON に比較値と校正理由を記録します。最前面と全 p95 予算は変更しません。
別途、履歴比で p50 が 20% を超えて劣化した場合も失敗とします。
既存の editor_eval と mock CLI の予算は変更していません。
実行コマンドは英語版の Run に記載しています。
cargo build --release --bin unity-cli 後、
scripts/e2e-matrix.py --suites perf に 2 Editor と release CLI を指定してください。
--perf-focus の既定値 both を使い、毎回新しい出力先を指定します。
専用プロジェクトの Assets/Scenes/Generated/E2E/Performance/ のみを変更し、
所有マーカーのないプロジェクトでは直接ベンチを実行できません。
このスイートだけなら LSP バイナリは不要です。
併せて既存の editor_eval ゲートも最前面で 100 回実行し、
perf-eval.json/perf-eval.log と同じ JSONL に別条件として記録します。
eval の既存の直接 TCP 経路は変更しません。
全結果を .unity/perf/editor-ops-history.jsonl に追記し、同じホスト・OS・CPU・
Unity・フォーカス・Play Mode 設定・計測方式で完全に計測できた直近 5 回の p50 中央値に
対して 20% を超える劣化でも失敗にします。5 回未満でも絶対予算は有効です。
予算超過でも完全な計測値は中央値に含め、操作エラーで不完全な回だけ除外します。
絶対予算はこのローリング中央値と独立に必ず適用します。
相対しきい値は UNITY_CLI_PERF_REGRESSION_PERCENT または
--regression-percent で上書きできます(絶対予算は無効化されません)。
二相の条件: 6000.3.25f1 背景では、同じバイナリでも遠隔操作の p50 が回ごとに
約 75 ms か約 150 ms になります(背景更新の待ち方が速い相と遅い相に分かれるため)。
両相をまとめた中央値では、速い相が中央値になった時点で遅い相の回が必ず失敗します(#461)。
そこで perf-budgets.json の two_phase_p50 に、この条件と単発の遠隔 20 操作を宣言します。
宣言した操作は次の手順で判定します。
- 条件が一致する完了計測すべての p50 を並べ、隣り合う値の比が最大の位置で二相に分ける
- 今回の p50 を、その境目の幾何平均より上か下かで相に割り当てる
- 同じ相の直近 5 回の中央値と比べ、+20% を超えたら失敗
観測が 5 回に満たない相は、条件全体が 5 回未満のときと同じく相対基準が未確立です (絶対予算は適用します)。境目の比がしきい値以下なら一相とみなし、従来どおり直近 5 回の 中央値で判定します。一相しか出ていない操作が初めて遅くなった場合は、これまでどおり失敗です。 しきい値、基準に使う 5 回、サンプル数、絶対予算は変えていません。 C# 読取(ローカル)、Play/Stop(複数回の更新をまたぐ)、2022.3.62f3 背景、最前面の 各条件は宣言しておらず、判定結果は従来と同じです。
基準が確立した相は、一様に 30% 遅くなれば失敗します。次の 3 点は限界として残り、絶対予算で抑えます。
速い相の回が境目(75/150 ms の相なら約 +35%)を超えて遅くなると、遅い相と区別できません。
初めて現れた遅い値が失敗したあと、その新しい相の続く 4 回は比較されません。
再計測で通すのではなく、最初の失敗を調べてください。
1 回の計測内で両相が混ざる操作(material_search、asset_delete、console)は p50 が
中間の値になり、履歴が短いうちは失敗することがあります。
Issue #397 の実履歴(tests/fixtures/perf/editor-ops-history-issue-397.jsonl)を再判定すると、
この条件でゲートが有効だった 9 回は従来すべて失敗、二相対応後は 6 回通過し、残り 3 回は
上記の混在する操作で失敗します。この種の失敗は低負荷で再計測してください。
宣言の変更は、同一バイナリで二相が確認できる履歴がある場合に限ります。
失敗時は violations と条件を確認し、低負荷で再計測して実際の回帰を修正します。
意図的な変更なら実測のレビュー後に該当予算だけを更新してください。
環境変更に伴う基準の作り直しは旧 JSONL を保存し、別の --history を指定します。
失敗を隠すための履歴削除や予算引き上げはしません。
リリースでは通常履歴・既定の 20% を使用します。
/release は 2 Editor × 2 フォーカス条件の perf PASS を必須 とします。
PR 本文にコマンド・Unity 版・pass/fail 件数・ログ要約を記録してください。
最終コード変更後に再実行し、古い PASS を流用しません。
既存 editor_eval 予算(最前面、p50 ≤ 50 ms / p95 ≤ 100 ms)と LSP ゲートも維持します。
benchmark.sh と perf-media-benchmark.sh は調査用で、上記ゲートの代わりにはなりません。
ベンチマーク・履歴ファイル:
tests/fixtures/skill-routing/benchmark.jsonl(ルーティング評価ベンチマーク: 127ケース).unity/skill-eval/skill-routing-history.jsonl(追記専用の評価履歴).unity/skill-eval/skill-static-report.json(最新の静的契約チェック結果)
静的検証(PR CI 必須):
cargo run -- skills lint --severity error予測JSONを使ったルーティング評価:
./scripts/skill-eval/llm-routing-eval.sh \
--model local-debug \
--predictions /path/to/predictions.jsonl外部ランナーコマンドを使ったルーティング評価:
./scripts/skill-eval/llm-routing-eval.sh \
--model nightly \
--runner-cmd '<your-runner-command>'ローカルの Codex runner を使ったルーティング評価:
./scripts/skill-eval/llm-routing-eval.sh \
--model codex-local \
--runner-cmd 'python3 scripts/skill-eval/run-codex-routing.py'この local runner には codex login と PATH 上の unity-cli が必要です。
unity-cli tool schema --output json をローカルで読み、スキルの例にない
ツール名とトップレベル引数もカタログへ補完します(Editor 不要)。
チェックアウトの実装を評価する場合は cargo build 後、runner command に
--unity-cli ./target/debug/unity-cli を追加します。schema 取得失敗時は
不完全なカタログで続行せず、その予測をエラーとして扱います。
現在の閾値:
top1 >= 0.90top2 >= 0.98tool_correct >= 0.92payload_valid >= 0.95
./scripts/publish.sh <major|minor|patch>を実行- 新しい
vX.Y.Zタグが push されたことを確認 .github/workflows/release.ymlでバイナリが公開されたことを確認- crates.io で crate が公開されたことを確認
詳細は RELEASE.md を参照してください。
ドキュメントと Issue-first 運用が現行の実装と矛盾していないことを定期的に確認します。
| ディレクトリ / ファイル | 内容 |
|---|---|
docs/architecture.md |
アーキテクチャ概要 |
docs/migration-notes.md |
移行と廃止の記録 |
docs/ |
開発ガイドと憲章 |
README.md |
プロジェクト概要 |
UnityCliBridge/Packages/unity-cli-bridge/README.md |
UPM パッケージドキュメント(英語) |
UnityCliBridge/Packages/unity-cli-bridge/README.ja.md |
UPM パッケージドキュメント(日本語) |
-
旧名称の残留確認: 以下のコマンドで
MCPや旧プロジェクト名の残留をチェックします。grep -rni "mcp" docs/ README.md | grep -v migration-notes.md
-
環境変数の整合確認: 本ドキュメントの変数一覧と
src/core/config.rsの実装を比較します。grep -oE 'UNITY_CLI_[A-Z_]+' src/core/config.rs | sort -u grep -oE 'UNITY_CLI_[A-Z_]+' docs/development.md | sort -u
-
ソースファイル構成の確認:
docs/architecture.mdのソースファイル一覧と実際のファイルを比較します。ls src/*.rs -
コマンド一覧の確認:
README.mdの Command Overview セクションがsrc/cli.rsのサブコマンド定義と一致しているか確認します。
- 新機能追加・設計変更を含む PR をマージする前
- 環境変数やコマンド体系に変更があった場合
- リリース前のチェックリストの一項目として
Unity 側コードベースは unity-mcp-server をベースコピーとし、差分は MCP→CLI 移行に必要な変更に限定します。方針と差分の記録は docs/migration-notes.md を参照してください。