@aefree/pi-unity 0.9.1 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project follows semantic versioning for public package releases.
7
7
 
8
+ ## [0.9.3] - 2026-08-10
9
+
10
+ ### Added
11
+
12
+ - Added an optional `automated` parameter to `unity_open_editor`, forwarding Unity Editor's `-automated` flag through both Unity CLI and direct Editor launch paths.
13
+
14
+ ## [0.9.2] - 2026-08-08
15
+
16
+ ### Changed
17
+
18
+ - Allow connected Pipeline tools to exit Play Mode by default when required, while retaining session-scoped opt-out control and verified lifecycle transitions.
19
+
8
20
  ## [0.9.1] - 2026-08-05
9
21
 
10
22
  ### Added
package/README.md CHANGED
@@ -1,110 +1,142 @@
1
1
  # Pi Unity
2
2
 
3
- Pi skill and tool package for reusable Unity workflows.
4
-
5
- ## Contents
6
-
7
- - skill: `unity-debugging`
8
- - skill: `unity-pipeline-workflows`
9
- - skill: `unity-batchmode-tests`
10
- - skill: `unity-interactive-playmode-authoring`
11
- - skill: `auditing-unity-agent-guidance`
12
- - tool: `unity_guidance_audit`
13
- - tool: `unity_project_status`
14
- - tool: `unity_pipeline_recompile`
15
- - tool: `unity_pipeline_run_tests`
16
- - tool: `unity_pipeline_eval`
17
- - tool: `unity_pipeline_inspect`
18
- - tool: `unity_inspect_artifacts`
19
- - tool: `unity_open_editor`
20
- - tool: `unity_launch_batchmode`
21
- - tool: `unity_run_test_batch`
22
- - commands: `/unity-open`, `/unity-playmode-exit`
23
-
24
- ## Skill boundaries
25
-
26
- Each packaged skill owns a distinct kind of Unity work:
27
-
28
- - `unity-debugging` owns reusable diagnostic strategy across Editor, runtime, package, asset, lifecycle, callback, and feature-activation problems.
29
- - `unity-pipeline-workflows` owns connected compilation and focused test execution through an already-running exact-copy Pipeline Editor.
30
- - `unity-batchmode-tests` owns isolated or report-producing Unity Test Framework execution.
31
- - `unity-interactive-playmode-authoring` owns explicit temporary inspection and tuning of live runtime state, followed by deliberate persistence when requested.
32
- - `auditing-unity-agent-guidance` owns review and migration of project-local Unity automation instructions.
33
-
34
- Operation-specific failure handling remains with the owning operational skill. `unity-debugging` owns reusable diagnostic strategy, not every troubleshooting instruction associated with Unity operations.
3
+ Pi tools and skills for reliable Unity Editor, Pipeline, batchmode, testing, and project-guidance workflows.
35
4
 
36
5
  ## Install
37
6
 
7
+ From npm:
8
+
9
+ ```bash
10
+ pi install npm:@aefree/pi-unity
11
+ ```
12
+
38
13
  From GitHub:
39
14
 
40
15
  ```bash
41
16
  pi install git:git@github.com:aefreedman/pi-unity.git
42
17
  ```
43
18
 
44
- Local development install:
19
+ For local development:
45
20
 
46
21
  ```bash
47
22
  pi install <path-to-pi-unity>
23
+ pi install -l <path-to-pi-unity> # project-local
48
24
  ```
49
25
 
50
- Project-local install:
26
+ Pi discovers the extension from `index.ts` and packaged skills from `skills/`.
51
27
 
52
- ```bash
53
- pi install -l <path-to-pi-unity>
54
- ```
28
+ ## Included tools
29
+
30
+ ### Connected Pipeline
31
+
32
+ Use these tools with an already-open exact Unity project copy that has a reachable `com.unity.pipeline` instance:
33
+
34
+ - `unity_project_status` — inspect lockfiles, matching Unity processes, Pipeline reachability, package version, and advertised commands without launching Unity.
35
+ - `unity_pipeline_recompile` — recompile through Pipeline with exact-copy preflight, bounded polling, and compact compiler evidence.
36
+ - `unity_pipeline_run_tests` — run one focused EditMode or PlayMode selection with bounded polling and aggregate results.
37
+ - `unity_pipeline_eval` — execute bounded project-specific C# through Pipeline's Roslyn REPL.
38
+ - `unity_pipeline_inspect` — dispatch supported package-owned inspection commands and return structured evidence.
39
+
40
+ Connected recompilation follows Unity's Script Changes While Playing policy and never preemptively sends `editor_stop`. Connected tests may exit Play Mode through advertised `editor_stop` when necessary, then verify Edit Mode before dispatch. Play Mode exit is allowed by default; `/unity-playmode-exit allow|disallow|status` controls the current session.
41
+
42
+ A timeout is uncertain: work may still be running. The tools do not silently cancel, retry, launch another Editor, or switch to batchmode.
43
+
44
+ ### Editor and batchmode
45
+
46
+ - `unity_open_editor` — open the Unity Editor GUI. Pass `automated: true` to add the Unity Editor `-automated` flag; this is distinct from the Unity CLI's own `--non-interactive` option.
47
+ - `unity_launch_batchmode` — run a bounded batchmode command through Unity CLI or the direct Editor executable.
48
+ - `unity_run_test_batch` — run one isolated or report-producing Unity Test Framework platform with generated XML and log paths.
49
+ - `unity_inspect_artifacts` — summarize existing Unity Test Framework XML and Unity logs without launching Unity.
50
+
51
+ Use connected tests when the exact project is already open and Pipeline testing is reachable. Use `unity_run_test_batch` for closed projects, CI-style isolation, categories or multiple filters, graphics-dependent PlayMode tests, or required NUnit XML/log evidence.
52
+
53
+ Batchmode runs use `-nographics` by default. Set `useGraphics: true` only for screenshots, visual capture, render checks, or graphics-dependent tests. Unity permits only one process per project folder, so all launch routes verify the exact project and use a per-project mutex.
54
+
55
+ ### Guidance audit
56
+
57
+ - `unity_guidance_audit` — inspect AGENTS.md, CLAUDE.md, Copilot, and Cursor instructions for outdated or unsafe Unity automation guidance without editing them.
58
+
59
+ ### Commands
60
+
61
+ - `/unity-open` — open the current Unity project copy or choose a nearby copy.
62
+ - `/unity-playmode-exit` — allow, disallow, or inspect Play Mode exit behavior for the current session.
63
+
64
+ ## Included skills
55
65
 
56
- ## Notes
57
-
58
- - Pi discovers packaged skills from `skills/` and extensions from `index.ts`.
59
- - `unity-debugging` provides general-purpose, evidence-first Unity diagnosis. For inactive features it routes agents through exact-version documentation, documented feature gates, and observable activation signals before project code, reflection, assembly searches, or Unity internals. Its UI Toolkit example checks the Game View Live Reload setting before callback diagnosis.
60
- - `@aefree/pi-project-artifacts` and `@aefree/pi-file-discovery` are independent optional peer integrations. When their Pi tools are active, pi-unity uses the capability contracts' global registry rendezvous to register a Unity solution-artifact profile candidate and/or generated-directory file-discovery filter. It never resolves optional peers from pi-unity's own module root, so separately installed Git/local packages compose correctly. Core Unity tools load without them; an advertised malformed registry contract fails visibly. Registrations are scoped, reverse-load-order safe, transactional across active integrations, and a delayed old-session shutdown cannot remove another scope's current records. Project artifacts remains schema-open: the Unity profile contributes applicability, definitions, validation, and confidence only; Unity metadata remains raw-filterable when this provider is absent. Provider development follows the [project-artifacts profile-provider contract](https://github.com/aefreedman/pi-project-artifacts/blob/main/docs/artifact-profile-providers.md).
61
- - The Unity filter recommends excluding `Library`, `Temp`, `Logs`, `obj`, `Build`, `Builds`, `UserSettings`, and `.vs` from broad project-root discovery. Broad exclusions declare `filterDecision: "applied"` with decision code `unity_broad_generated_directories_applied`; an exact generated or `Library/PackageCache/...` root is searched with the explicit `filterDecision: "bypassed"` code `unity_exact_generated_root_bypassed`, rather than a compatibility sentinel glob. File-discovery filter failures degrade discovery filtering rather than authorizing or blocking inspection; the canonical file-discovery package remains Unity-neutral.
62
- - The optional artifact profile describes `engine`, `unity_version`, `unity_packages`, `render_pipeline`, and `platforms` for solution and memory artifacts. Every field is optional; present fields receive type/enum validation while undeclared project metadata remains open. Artifact paths already distinguish solutions from memories, and evolving project vocabulary belongs in generic `tags`, `module`, or `component` fields rather than a Unity-owned document taxonomy. The profile contributes only when the workspace has Unity `ProjectVersion.txt` evidence, so a conventional artifact path never selects it by itself.
63
- - `unity_guidance_audit` performs a bounded, read-only scan of AGENTS.md, CLAUDE.md, Copilot, and Cursor guidance for outdated Unity CLI/Pipeline, batchmode, test, lifecycle, and exact-project-copy instructions. The `auditing-unity-agent-guidance` skill owns contextual migration and user-authorized edits.
64
- - `unity_open_editor` launches the full Unity Editor GUI.
65
- - `unity_open_editor` prefers the installed `unity open` CLI when available, falling back to direct editor executable launch.
66
- - `unity_project_status` reports native Unity lockfile state, Unity CLI status output, running Unity processes, the locally declared `com.unity.pipeline` version, exact-project-copy Pipeline instances, and bounded live advertised commands without launching Unity. Pipeline discovery has distinct `absent`, `timeout`, and `unavailable` states; a timeout is startup uncertainty rather than proof of absence. Rendered process command lines redact access tokens and credential-like values.
67
- - `unity_inspect_artifacts` summarizes existing Unity Test Framework XML results and Unity logs without launching Unity, reducing ad hoc shell parsing after failures.
68
- - Planning and test routing preserve the exact project copy: a reachable Pipeline Editor is a positive connected inspection surface, and the project should run connected tests without closing the Editor or replacing it with a second Editor. Other connected operations use that same exact copy. `unity_pipeline_eval` rechecks canonical identity and advertised `eval` immediately before dispatch and is the general REPL escape hatch for project-specific properties and questions that registered commands did not anticipate. `unity_pipeline_inspect` exposes the package-owned purpose-built inspection commands when their structured results fit the question. Tooling should bound the request and result, preserve exact-copy evidence, and distinguish reads from mutations—not maintain a brittle API-property allowlist or pretend arbitrary C# can be proven read-only from syntax alone.
69
- - `unity-pipeline-workflows` routes focused connected work through `unity_pipeline_recompile` and `unity_pipeline_run_tests`. Each performs exact-copy preflight, advertised-command checks, lifecycle inspection, identity-aware bounded internal polling, and compact output in one model-visible call. `unity_pipeline_recompile` never preemptively sends `editor_stop`: while Play Mode is active it honors Unity's Script Changes While Playing policy (continue, stop-and-recompile, or defer) when future `editor_status` payloads expose it, and reports unavailable policy as uncertainty. Connected tests retain a separate explicit lifecycle guard. Pipeline `no_tests`/idle status is treated as safe pre-dispatch inactivity rather than uncertainty. Timeouts are uncertain and do not cancel, retry, close Unity, or switch to batchmode. Connected tests do not inherently produce NUnit XML.
70
- - `unity_run_test_batch` is the preferred isolated/report-producing Unity Test Framework entry point, not a reason to close a reachable Pipeline Editor. Choose it for a closed project, intentional CI isolation, category/multiple filters unsupported by the single connected test-name filter, or required NUnit XML/log artifacts. It runs exactly one EditMode or PlayMode batch, combines filter/category arrays into one launch, creates collision-safe absolute XML/log paths under the project `Logs` directory, omits `-quit`, and uses the same guarded launcher as `unity_launch_batchmode`.
71
- - `unity_launch_batchmode` prefers the installed `unity run` CLI when available, falling back to direct editor executable batchmode launch; use it when custom raw Editor arguments are required.
72
- - `unity_launch_batchmode` adds `-nographics` by default to avoid unnecessary graphics initialization and reduce focus stealing; set `useGraphics: true` only for screenshots, visual capture, render checks, or graphics-dependent PlayMode tests.
73
- - Unity GUI, generic batchmode, and test-batch tools expose `launcher` (`auto`, `unity-cli`, or `editor-executable`) so workflows can force direct Editor execution when Unity CLI argument handling differs from `Unity.exe`/`Unity`. Every launch route keeps same-project process verification and a per-project mutex; unknown process state blocks launch. Direct Editor execution blocks native lockfiles, while Unity CLI may handle a stale lockfile only after pi-unity verifies no matching project process.
74
- - In Unity CLI mode, `unity_launch_batchmode` forwards args after `unity run <project> --` and strips direct-Editor flags managed by the CLI (`-batchmode`, `-projectPath`, `-quit`).
75
- - `unity_run_test_batch`, `unity_launch_batchmode`, and `unity_inspect_artifacts` require parsed Unity Test Framework results to report a known positive executed-test count and no failures before treating them as passing evidence. Zero-test, unknown-total, missing-result, malformed-result, and failing batches are non-passing; full artifacts remain on disk while session details retain bounded excerpts and byte counts.
76
- - Validation guidance treats explicit user/project PlayMode skips as authoritative, distinguishes baseline compile/EditMode evidence from optional PlayMode evidence, and stops unchanged relaunch loops after hangs or infrastructure failures in favor of one inspection of the exact current-run artifact paths.
77
- - `unity_launch_batchmode` uses Unity CLI status and direct process scans before launch. In Unity CLI mode, stale native `Temp/UnityLockfile` detection is delegated to `unity run`; direct Editor executable mode still blocks on the native lockfile for safety. A Pi-side project mutex prevents duplicate packaged batchmode calls from spawning Unity concurrently.
78
- - `unity_launch_batchmode` can close a same-project blocking Unity process only when isolated execution was deliberately selected, the tool call sets `closeBlockingUnityProcess: true`, and Pi settings enable `piUnity.allowCloseRunningUnityProcess`. Do not use this to replace reachable connected Pipeline testing. The tool re-scans and selects matching Unity processes itself; it never accepts a model-supplied PID.
79
- - After a guarded same-call close, `unity_launch_batchmode` may remove the exact resolved project's stale `Temp/UnityLockfile` only after verifying no matching Unity process remains. It still refuses general lockfile deletion outside that guarded continuation.
80
- - When using `closeBlockingUnityProcess: true`, prefer `launcher: "auto"` or `launcher: "unity-cli"`; force `launcher: "editor-executable"` only when direct Editor execution is explicitly required.
81
- - If a Unity launch is blocked by a lockfile, run `unity_project_status` before asking a user to remove anything.
82
- - `/unity-open` is the user-facing GUI launcher helper.
83
- - The package resolves Unity project copies from a direct project root, a coordination root containing multiple copies, or another nearby folder. Pipeline routing validates canonical project-path identity after CLI discovery so similarly named copies are not treated as interchangeable; connected commands always receive the exact resolved project path.
84
- - Installing and starting `com.unity.pipeline@0.3.1-exp.1` is a broader project mutation than adding its manifest entry: its server startup assigns `Application.runInBackground = true`, which Unity persists as `PlayerSettings.runInBackground` in `ProjectSettings/ProjectSettings.asset`. Review that tracked change alongside `manifest.json` and `packages-lock.json`.
85
- - Unity install probing is OS-aware and avoids machine-specific assumptions by using the project's `ProjectSettings/ProjectVersion.txt`, the optional `unity` CLI, standard per-OS install locations, and optional `UNITY_EDITOR_PATH` overrides.
86
- - Unity allows only one process per project folder; GUI and batchmode both count.
87
- - The `unity-batchmode-tests` skill is intended for Unity Test Framework CLI runs.
88
- - Keep skill-specific references and helper assets under the skill directory beside `SKILL.md`.
89
-
90
- ## Connected Pipeline and `eval` policy
91
-
92
- Registered Pipeline commands are ergonomic shortcuts for anticipated workflows. Advertised `eval` covers the operations and inspections that were not anticipated: it compiles C# with Roslyn, runs it on the connected Editor's main thread, and returns the result. This is a live REPL into the exact running project, not merely a restricted planning expression evaluator.
93
-
94
- Use the registered `unity_pipeline_eval` tool with bounded C# `code`, for example:
66
+ Each skill owns a distinct workflow:
67
+
68
+ - `unity-debugging` evidence-first diagnosis of Editor, runtime, package, asset, lifecycle, callback, and feature-activation problems.
69
+ - `unity-pipeline-workflows` connected compilation and focused tests through an already-running exact-copy Pipeline Editor.
70
+ - `unity-batchmode-tests` isolated or report-producing Unity Test Framework execution.
71
+ - `unity-interactive-playmode-authoring` temporary live runtime inspection and tuning followed by deliberate persistence when requested.
72
+ - `auditing-unity-agent-guidance` review and migration of project-local Unity automation instructions.
73
+
74
+ Operation-specific recovery belongs to the operational skill. `unity-debugging` supplies the reusable diagnostic strategy rather than duplicating every workflow's failure handling.
75
+
76
+ ## Choosing a workflow
77
+
78
+ | Situation | Preferred route |
79
+ | --- | --- |
80
+ | Open exact-copy Editor with reachable Pipeline | Connected Pipeline tools |
81
+ | Closed project or intentional CI isolation | `unity_run_test_batch` or `unity_launch_batchmode` |
82
+ | Required NUnit XML or Unity log evidence | `unity_run_test_batch` |
83
+ | Existing failed-run artifacts | `unity_inspect_artifacts` |
84
+ | Project-specific C# query or operation | `unity_pipeline_eval` |
85
+ | Supported structured project inspection | `unity_pipeline_inspect` |
86
+ | Open the GUI explicitly | `unity_open_editor` or `/unity-open` |
87
+
88
+ Pass an explicit project `path` when multiple copies may be discovered. Pipeline routing compares canonical paths so similarly named copies are not treated as interchangeable.
89
+
90
+ ## Pipeline safety and evidence
91
+
92
+ The connected compile and test tools:
93
+
94
+ - require advertised commands before dispatch;
95
+ - verify the exact project copy and Pipeline identity;
96
+ - poll internally with fixed deadlines and bounded backoff;
97
+ - reject malformed or semantically failing nested results;
98
+ - require a known positive test count and zero failures before reporting a pass;
99
+ - discard passing-test records while retaining bounded failure diagnostics;
100
+ - detect pre-existing or clearly displaced test runs when available correlation fields permit it.
101
+
102
+ Another connected client is not a project lock. When Pipeline returns stable correlation fields, conflicting status is reported as displaced and uncertain. If Pipeline omits stable run identity, a competing same-mode, same-filter run may be indistinguishable from the requested run; the tool cannot prove exclusive ownership from shared Editor status alone.
103
+
104
+ ### Pipeline eval
105
+
106
+ `unity_pipeline_eval` compiles C# with Roslyn and runs it on the connected Editor main thread. It is a live REPL, not an expression-only or statically read-only evaluator.
95
107
 
96
108
  ```text
97
109
  { code: "return UnityEditor.EditorSettings.scriptChangesDuringPlay;" }
98
110
  { code: "var s = UnityEngine.Application.dataPath; return s.Length;" }
99
111
  ```
100
112
 
101
- Use `unity_pipeline_inspect` for purpose-built connected reads such as `editor_status` or `get_scene_hierarchy`; eval is intentionally owned only by `unity_pipeline_eval`.
113
+ Use `unity_pipeline_inspect` when a purpose-built structured command fits. Use eval for bounded project-specific work that matches the user's intent. Prefer typed tools when they provide stronger lifecycle, polling, validation, or recovery semantics.
114
+
115
+ ## Launch and process safeguards
116
+
117
+ `unity_open_editor` and batchmode tools prefer the installed Unity CLI and can fall back to the direct Editor executable. Set `launcher` to `auto`, `unity-cli`, or `editor-executable` when explicit routing is needed.
118
+
119
+ Before launching, pi-unity checks:
120
+
121
+ - running Unity processes targeting the exact project;
122
+ - Unity CLI status and Pipeline instances;
123
+ - native `Temp/UnityLockfile` state;
124
+ - the package-owned per-project launch mutex.
102
125
 
103
- Because `eval` reaches the same engine and Editor APIs as project code, its security token and exact-copy identity are meaningful trust boundaries. A static snippet allowlist is not: ordinary property getters can call code, while apparently simple expressions can still have side effects. Pi-unity therefore treats declared task intent as the boundary: regular inspection through `unity_pipeline_eval` is allowed; mutations must match the user's request; lifecycle, destructive, persistent-setting, asset, scene-save, package, build, and test changes require the same explicit authorization they would through a typed command. Typed tools remain preferred when they provide better validation, polling, compact evidence, or recovery semantics, but they are assistance rather than exclusive gateways. Results and diagnostics remain bounded, and an uncertain dispatch is never silently retried through another route.
126
+ Unknown process state blocks launch. Direct Editor execution blocks native lockfiles. Unity CLI may handle a stale lockfile only after pi-unity verifies that no matching Unity process remains.
127
+
128
+ A batchmode call may close a matching Unity process only when all of the following are true:
129
+
130
+ 1. isolated execution was deliberately selected;
131
+ 2. the call sets `closeBlockingUnityProcess: true`;
132
+ 3. `piUnity.allowCloseRunningUnityProcess` is enabled;
133
+ 4. any configured test-only restriction permits the operation.
134
+
135
+ The package selects and revalidates the process itself; it never accepts a model-supplied PID. It may remove only the exact project's stale lockfile after a same-call guarded closure and verification that no matching process remains.
104
136
 
105
137
  ## Settings
106
138
 
107
- `pi-unity` reads optional package-specific settings from global `~/.pi/agent/settings.json` and, for trusted projects, project `.pi/settings.json`:
139
+ Pi-unity reads optional settings from global `~/.pi/agent/settings.json` and, for trusted projects, project `.pi/settings.json`:
108
140
 
109
141
  ```json
110
142
  {
@@ -116,22 +148,43 @@ Because `eval` reaches the same engine and Editor APIs as project code, its secu
116
148
  }
117
149
  ```
118
150
 
119
- - `allowCloseRunningUnityProcess` defaults to `false`. When enabled, `unity_launch_batchmode` may close only Unity processes that target the resolved project and only when the tool call explicitly sets `closeBlockingUnityProcess: true`.
120
- - `closeRunningUnityProcessOnlyForTests` defaults to `true`, limiting process closure to Unity Test Framework launches (`-runTests`).
121
- - `closeRunningUnityProcessTimeoutMs` defaults to `30000` and is clamped between 1000 and 120000 milliseconds.
151
+ - `allowCloseRunningUnityProcess` defaults to `false`.
152
+ - `closeRunningUnityProcessOnlyForTests` defaults to `true`.
153
+ - `closeRunningUnityProcessTimeoutMs` defaults to `30000` and is clamped from 1000 to 120000 milliseconds.
122
154
 
123
- Autonomous Play Mode exit is a separate session-scoped toggle and defaults to disallowed. Use `/unity-playmode-exit allow` only to authorize package-owned typed lifecycle operations that may exit Play Mode, `/unity-playmode-exit disallow` to restore the default, or `/unity-playmode-exit status` to inspect it. `unity_pipeline_recompile` never sends `editor_stop` and does not override Unity's Script Changes While Playing preference: a known continue/defer policy needs no exit authorization, a known stop-and-recompile policy does, and a missing policy is conservatively treated as potentially exiting. `unity_pipeline_run_tests` retains its separate verified `editor_stop` lifecycle path when authorized. The choice is recorded in the current session branch so it survives reload/resume, but it is not a global or project setting. Output/details identify explicit agent exit separately from Unity-policy-driven or unavailable-policy behavior; pi-unity never enters Play Mode autonomously.
155
+ ## Optional integrations
124
156
 
125
- ## Skill evaluation
157
+ `@aefree/pi-project-artifacts` and `@aefree/pi-file-discovery` are optional peer integrations. Core Unity tools work without them.
126
158
 
127
- The `auditing-unity-agent-guidance` skill has an opt-in behavioral eval under `evals/auditing-unity-agent-guidance/`. It runs isolated fixtures through Pi, checks triggering, filesystem outcomes, instruction fidelity, and tool-call efficiency, and can compare skill-enabled runs with a no-skill baseline. Because it invokes an agent and may incur provider costs, it is not part of `npm test`.
159
+ Pi-unity uses a global registry rendezvous so independently installed Git, local, or npm packages can compose without sibling source paths:
128
160
 
129
- ```bash
130
- npm run eval:guidance-skill -- --cases audit_legacy_instructions,migrate_mixed_harness_guidance,unrelated_typescript_review
131
- npm run eval:guidance-skill -- --condition both --trials 3
161
+ - The project-artifacts integration contributes an optional Unity profile for solution and memory metadata.
162
+ - The file-discovery integration recommends excluding generated Unity directories from broad searches while preserving exact searches inside those directories.
163
+
164
+ The optional peer integrations are session-scoped, reverse-load-order safe, and transactional. A malformed advertised integration contract fails visibly; an unavailable optional package does not prevent the Unity extension from loading.
165
+
166
+ ### Optional artifact metadata
167
+
168
+ When project artifacts are active, solution and memory Markdown may use:
169
+
170
+ ```yaml
171
+ ---
172
+ engine: unity
173
+ unity_version: "6000.0"
174
+ unity_packages:
175
+ - com.unity.inputsystem
176
+ render_pipeline: urp
177
+ platforms:
178
+ - windows
179
+ - android
180
+ ---
132
181
  ```
133
182
 
134
- See `evals/auditing-unity-agent-guidance/README.md` for the rubric.
183
+ Supported `render_pipeline` values are `builtin`, `urp`, `hdrp`, `custom`, and `agnostic`. All fields are optional, and undeclared project metadata remains open and raw-filterable.
184
+
185
+ ### File-discovery filtering
186
+
187
+ Broad Unity project searches may exclude `Library`, `Temp`, `Logs`, `obj`, `Build`, `Builds`, `UserSettings`, and `.vs`. An exact generated root—including `Library/PackageCache/...`—remains searchable. Filter failures degrade filtering rather than blocking inspection.
135
188
 
136
189
  ## Package layout
137
190
 
@@ -139,58 +192,42 @@ See `evals/auditing-unity-agent-guidance/README.md` for the rubric.
139
192
  pi-unity/
140
193
  index.ts
141
194
  src/
142
- unity-core.ts
195
+ unity-artifact-profile.ts
143
196
  unity-batchmode.ts
144
197
  unity-cli.ts
198
+ unity-core.ts
199
+ unity-file-discovery-filter.ts
200
+ unity-guidance-audit.ts
145
201
  unity-launch.ts
202
+ unity-pipeline.ts
146
203
  unity-processes.ts
147
204
  unity-project-lock.ts
148
205
  unity-projects.ts
206
+ unity-test-batch.ts
149
207
  skills/
150
- unity-debugging/
151
- SKILL.md
152
208
  auditing-unity-agent-guidance/
153
- SKILL.md
154
- references/
155
- assets/
156
- unity-pipeline-workflows/
157
- SKILL.md
158
209
  unity-batchmode-tests/
159
- SKILL.md
210
+ unity-debugging/
211
+ unity-interactive-playmode-authoring/
212
+ unity-pipeline-workflows/
160
213
  tests/
161
214
  ```
162
215
 
163
- ## Optional artifact metadata
164
-
165
- When `@aefree/pi-project-artifacts` is also active in a Unity workspace, solution and memory Markdown may use the following profile-enriched fields:
166
-
167
- ```yaml
168
- ---
169
- engine: unity
170
- unity_version: "6000.0"
171
- unity_packages:
172
- - com.unity.inputsystem
173
- render_pipeline: urp
174
- platforms:
175
- - windows
176
- - android
177
- ---
178
- ```
179
-
180
- Supported `render_pipeline` values are `builtin`, `urp`, `hdrp`, `custom`, and `agnostic`. Keep `unity_version` quoted because Unity versions are identifiers rather than numeric values. Omit inapplicable fields instead of adding placeholders. These fields improve exact retrieval and diagnostics but are not required for indexing or raw filtering.
181
-
182
- ## Testing
216
+ ## Development and validation
183
217
 
184
218
  ```bash
219
+ npm ci
185
220
  npm test
186
- npm pack --dry-run
221
+ npm pack --dry-run --json
187
222
  ```
188
223
 
189
- The package declares optional peer integrations for `@aefree/pi-project-artifacts` and `@aefree/pi-file-discovery`; install only the integrations needed for artifact profiles or file-discovery filtering. The standalone Unity tools and skills do not require them. The package archive contains no copied dependency tree, sibling `file:` path, or workspace link.
224
+ The auditing skill also has an opt-in provider-backed behavioral eval under `evals/auditing-unity-agent-guidance/`; it is intentionally not part of `npm test`.
225
+
226
+ The registry-clean `package-lock.json` is committed. Optional development packages resolve from the public registry, and the npm archive contains no copied dependency tree, sibling `file:` dependency, or workspace link.
190
227
 
191
- ## Release status
228
+ ## Unity Pipeline project side effect
192
229
 
193
- A registry-clean `package-lock.json` is committed, optional development packages resolve from the public registry, and the manifest is prepared for public scoped publication. Publishing remains a separate manual operation requiring npm authentication and explicit authorization.
230
+ Starting `com.unity.pipeline@0.3.1-exp.1` assigns `Application.runInBackground = true`, which Unity persists as `PlayerSettings.runInBackground` in `ProjectSettings/ProjectSettings.asset`. Review that tracked change alongside `manifest.json` and `packages-lock.json` when installing Pipeline in a Unity project.
194
231
 
195
232
  ## License
196
233
 
package/index.ts CHANGED
@@ -103,6 +103,7 @@ type UnityLauncherPreference = "auto" | "unity-cli" | "editor-executable";
103
103
  const OPEN_EDITOR_PARAMS = Type.Object({
104
104
  path: Type.Optional(Type.String({ description: "Unity project path, workspace copy root, or folder containing project copies." })),
105
105
  unityEditorPath: Type.Optional(Type.String({ description: "Optional explicit Unity executable path override." })),
106
+ automated: Type.Optional(Type.Boolean({ default: false, description: "Pass Unity Editor's -automated flag when opening the project. Defaults to false." })),
106
107
  launcher: LAUNCHER_SCHEMA,
107
108
  });
108
109
 
@@ -571,7 +572,7 @@ async function buildProjectStatusReport(
571
572
  ctx: ExtensionContext,
572
573
  candidate: UnityProjectCandidate,
573
574
  signal?: AbortSignal,
574
- allowAutonomousPlayModeExit = false,
575
+ allowAutonomousPlayModeExit = true,
575
576
  ): Promise<{ text: string; details: UnityToolDetails }> {
576
577
  const lockState = await inspectUnityProjectBusyState(candidate.projectRoot);
577
578
  const cliStatus = await listRunningUnityCliEditorsForProject(candidate.projectRoot);
@@ -597,7 +598,7 @@ async function buildProjectStatusReport(
597
598
  `- Pipeline command discovery: ${cliCapabilities.commandDiscoverySucceeded ? `${cliCapabilities.advertisedCommands.length}/${cliCapabilities.advertisedCommandCount} command(s) reported${cliCapabilities.advertisedCommandsTruncated ? " (bounded/truncated)" : ""}` : cliCapabilities.commandDiscovery}`,
598
599
  `- piUnity.allowCloseRunningUnityProcess: ${piUnitySettings.allowCloseRunningUnityProcess ? "enabled" : "disabled"}`,
599
600
  `- piUnity.closeRunningUnityProcessOnlyForTests: ${piUnitySettings.closeRunningUnityProcessOnlyForTests ? "enabled" : "disabled"}`,
600
- `- Session autonomous Play Mode exit: ${allowAutonomousPlayModeExit ? "allowed" : "disallowed (default)"}`,
601
+ `- Session Play Mode exit: ${allowAutonomousPlayModeExit ? "allowed" : "disabled"}`,
601
602
  ];
602
603
 
603
604
  if (runningProcesses.length > 0) {
@@ -1192,9 +1193,9 @@ export default function freeUnityPi(pi: ExtensionAPI) {
1192
1193
  // Lifecycle handles are session-scoped.
1193
1194
  const registrations = new WeakMap<object, ScopeRegistrations>();
1194
1195
  const playModeExitAuthorization = new WeakMap<object, boolean>();
1195
- const sessionAllowsAutonomousPlayModeExit = (ctx: ExtensionContext): boolean => playModeExitAuthorization.get(ctx.sessionManager) ?? false;
1196
+ const sessionAllowsAutonomousPlayModeExit = (ctx: ExtensionContext): boolean => playModeExitAuthorization.get(ctx.sessionManager) ?? true;
1196
1197
  const restoreSessionSettings = (ctx: ExtensionContext): void => {
1197
- let allowed = false;
1198
+ let allowed = true;
1198
1199
  const getBranch = (ctx.sessionManager as { getBranch?: () => Array<{ type: string; customType?: string; data?: unknown }> }).getBranch;
1199
1200
  for (const entry of getBranch?.call(ctx.sessionManager) ?? []) {
1200
1201
  if (entry.type !== "custom" || entry.customType !== "pi-unity-session-settings-v1") continue;
@@ -1202,7 +1203,7 @@ export default function freeUnityPi(pi: ExtensionAPI) {
1202
1203
  if (typeof data?.allowAutonomousPlayModeExit === "boolean") allowed = data.allowAutonomousPlayModeExit;
1203
1204
  }
1204
1205
  playModeExitAuthorization.set(ctx.sessionManager, allowed);
1205
- ctx.ui.setStatus?.("pi-unity-playmode-exit", allowed ? "Unity Play Mode exit: allowed" : undefined);
1206
+ ctx.ui.setStatus?.("pi-unity-playmode-exit", allowed ? undefined : "Unity Play Mode exit: disabled");
1206
1207
  };
1207
1208
  const unregisterScope = (current: ScopeRegistrations | undefined): boolean => {
1208
1209
  if (current === undefined) return false;
@@ -1264,7 +1265,7 @@ export default function freeUnityPi(pi: ExtensionAPI) {
1264
1265
  if (changed) pi.events.emit("pi-unity:capabilities-changed", { scope, contractVersion: 1, action: "unregistered" });
1265
1266
  });
1266
1267
  pi.registerCommand("unity-playmode-exit", {
1267
- description: "Allow, disallow, or show autonomous Play Mode exit for this Pi session (default: disallowed).",
1268
+ description: "Allow, disable, or show Play Mode exit behavior for this Pi session (default: allowed).",
1268
1269
  getArgumentCompletions: (prefix: string) => ["allow", "disallow", "status"]
1269
1270
  .filter((value) => value.startsWith(prefix.trim().toLowerCase()))
1270
1271
  .map((value) => ({ value, label: value })),
@@ -1278,8 +1279,8 @@ export default function freeUnityPi(pi: ExtensionAPI) {
1278
1279
  }
1279
1280
  const allowed = sessionAllowsAutonomousPlayModeExit(ctx);
1280
1281
  if (action !== "status") pi.appendEntry("pi-unity-session-settings-v1", { allowAutonomousPlayModeExit: allowed });
1281
- ctx.ui.setStatus?.("pi-unity-playmode-exit", allowed ? "Unity Play Mode exit: allowed" : undefined);
1282
- ctx.ui.notify(`Autonomous Unity Play Mode exit is ${allowed ? "allowed" : "disallowed"} for this session.`, allowed ? "warning" : "info");
1282
+ ctx.ui.setStatus?.("pi-unity-playmode-exit", allowed ? undefined : "Unity Play Mode exit: disabled");
1283
+ ctx.ui.notify(`Unity Play Mode exit is ${allowed ? "allowed" : "disabled"} for this session.`, allowed ? "info" : "warning");
1283
1284
  },
1284
1285
  });
1285
1286
 
@@ -1397,7 +1398,7 @@ export default function freeUnityPi(pi: ExtensionAPI) {
1397
1398
  promptSnippet: "Recompile an already-open Unity Pipeline project in one bounded connected call without shell polling.",
1398
1399
  promptGuidelines: [
1399
1400
  "Use unity_pipeline_recompile for connected recompilation of an already-open exact Unity project copy instead of raw Unity CLI status loops.",
1400
- "unity_pipeline_recompile never sends editor_stop. In Play Mode it honors Unity's Script Changes While Playing policy; /unity-playmode-exit allow is required only when that policy may exit Play Mode or Pipeline does not expose it.",
1401
+ "unity_pipeline_recompile never sends editor_stop. In Play Mode it honors Unity's Script Changes While Playing policy, including policies that may exit Play Mode.",
1401
1402
  "unity_pipeline_recompile never launches, closes, saves, retries, cancels Unity, or overrides Unity's script-change policy; its timeout means the operation may still be running.",
1402
1403
  ],
1403
1404
  parameters: PIPELINE_RECOMPILE_PARAMS,
@@ -1424,7 +1425,7 @@ export default function freeUnityPi(pi: ExtensionAPI) {
1424
1425
  promptSnippet: "Run focused connected Unity EditMode or PlayMode tests in one bounded call without shell polling; aggregate passing results stay compact.",
1425
1426
  promptGuidelines: [
1426
1427
  "Use unity_pipeline_run_tests for one focused connected Unity test platform when the exact Editor is already open and reachable.",
1427
- "unity_pipeline_run_tests retains a separate lifecycle guard: it exits Play Mode only when the user enabled /unity-playmode-exit allow for the current session; autonomous exit is disallowed by default.",
1428
+ "unity_pipeline_run_tests may exit Play Mode through advertised editor_stop when needed, then verifies Edit Mode before dispatching tests.",
1428
1429
  "Use unity_run_test_batch instead of unity_pipeline_run_tests for closed projects, isolation, complex filters/categories, or required NUnit XML/log evidence.",
1429
1430
  "unity_pipeline_run_tests does not cancel uncertain work or switch to batchmode after timeout; report that the connected run may still be running.",
1430
1431
  ],
@@ -1578,7 +1579,7 @@ export default function freeUnityPi(pi: ExtensionAPI) {
1578
1579
  pi.registerTool({
1579
1580
  name: "unity_open_editor",
1580
1581
  label: "Unity Open Editor",
1581
- description: "Open the Unity Editor GUI for a Unity project copy.",
1582
+ description: "Open the Unity Editor GUI for a Unity project copy, optionally passing Unity Editor's -automated flag.",
1582
1583
  promptSnippet: "Open the Unity Editor GUI for a resolved Unity project when the user explicitly asks for the editor to open.",
1583
1584
  promptGuidelines: [
1584
1585
  "Use this tool only when the user explicitly wants the Unity Editor GUI opened.",
@@ -1605,11 +1606,12 @@ export default function freeUnityPi(pi: ExtensionAPI) {
1605
1606
  launch = launchUnityCliOpenDetached(candidate.projectRoot, {
1606
1607
  editorVersion: candidate.unityVersion,
1607
1608
  editorPath: params.unityEditorPath,
1609
+ automated: params.automated,
1608
1610
  });
1609
1611
  } else {
1610
1612
  await assertUnityProjectNotBusy(candidate.projectRoot);
1611
1613
  editorPath = await resolveUnityEditorPath(candidate.unityVersion, { overridePath: params.unityEditorPath });
1612
- launch = launchUnityEditorDetached(editorPath, candidate.projectRoot);
1614
+ launch = launchUnityEditorDetached(editorPath, candidate.projectRoot, { automated: params.automated });
1613
1615
  }
1614
1616
  const text = buildEditorLaunchSummary(ctx.cwd, candidate, editorPath, discoveryWarning, launcher);
1615
1617
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aefree/pi-unity",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./index.ts"
@@ -71,7 +71,7 @@ A request to persist authorizes the durable source/asset edit, but lifecycle cha
71
71
  1. Capture the final runtime values and the owning coordinate space, dimensions, scale, or other context needed to convert them into authored values.
72
72
  2. Identify the authoritative source or serialized field and trace how it becomes the runtime value. Do not write a visual coordinate into a guessed representation.
73
73
  3. Determine whether persistence triggers asset import, script compilation, domain reload, or runtime reconstruction, and whether the current instance can consume the change.
74
- 4. Inspect `editor_status`. If Play Mode must stop, obtain lifecycle authorization unless already explicit (including the current session's `/unity-playmode-exit allow` toggle), dispatch the advertised `editor_stop`, and verify Play Mode exited.
74
+ 4. Inspect `editor_status`. If Play Mode must stop, dispatch the advertised `editor_stop` and verify Play Mode exited.
75
75
  5. Apply the durable edit with the owning file/asset tool.
76
76
  6. Use the `unity-pipeline-workflows` skill for any required connected compilation and terminal status validation.
77
77
  7. Re-enter Play Mode only when requested. Report temporary live confirmation and durable verification as separate evidence.
@@ -22,7 +22,7 @@ A timeout or malformed response is uncertain: the Unity operation may still be r
22
22
 
23
23
  1. Pass an explicit `path` when multiple project copies may be found; paths identify copies, not display names.
24
24
  2. The typed tools require a reachable exact-copy Pipeline and advertised `editor_status` plus operation commands. A different connected client is not itself a project lock.
25
- 3. Autonomous Play Mode exit is disallowed by default. `unity_pipeline_recompile` never sends `editor_stop` or overrides Unity's Script Changes While Playing preference: known recompile-and-continue and defer policies may proceed without exit authorization; known stop-and-recompile and unavailable policy require `/unity-playmode-exit allow` because the recompile may exit Play Mode. Pipeline 0.4 does not currently expose that preference, so absence is reported as uncertainty rather than a claim that Unity cannot compile in Play Mode. `unity_pipeline_run_tests` has separate lifecycle semantics and may dispatch advertised `editor_stop` only after that same authorization, then verifies Edit Mode. `/unity-playmode-exit disallow` restores the default. The tools never enter Play Mode, pause, save, launch, or close Unity autonomously; recompilation may perform Unity's normal asset refresh/import and script-change behavior.
25
+ 3. `unity_pipeline_recompile` never sends `editor_stop` or overrides Unity's Script Changes While Playing preference. Known recompile-and-continue, stop-and-recompile, and defer policies proceed according to Unity's configured behavior. Pipeline 0.4 does not currently expose that preference, so the tool reports the unavailable policy while allowing recompilation to proceed. `unity_pipeline_run_tests` may dispatch advertised `editor_stop` when needed, then verifies Edit Mode before running tests. The tools never enter Play Mode, pause, save, launch, or close Unity; recompilation may perform Unity's normal asset refresh/import and script-change behavior.
26
26
  4. Test success requires a well-formed terminal result, a known positive executed count, and zero failures. An asynchronous initiation with `Total: 0` and `running` is nonterminal.
27
27
  5. Passing test records are intentionally discarded. Failures retain only a bounded set of failed/inconclusive names, messages, and stack excerpts.
28
28
 
package/src/unity-cli.ts CHANGED
@@ -18,6 +18,8 @@ export type UnityCliLaunchOptions = {
18
18
  timeoutSeconds?: number;
19
19
  cliCommand?: string;
20
20
  useGraphics?: boolean;
21
+ /** Forward Unity Editor's -automated flag through `unity open --args`. */
22
+ automated?: boolean;
21
23
  };
22
24
 
23
25
  export type UnityCliPipelineInstance = {
@@ -80,6 +82,7 @@ function appendUnityCliEditorOptions(args: string[], options: UnityCliLaunchOpti
80
82
  export function createUnityCliOpenCommand(projectRoot: string, options: UnityCliLaunchOptions = {}): UnityCliCommand {
81
83
  const args = [...unityCliBaseArgs(), "open", projectRoot];
82
84
  appendUnityCliEditorOptions(args, options);
85
+ if (options.automated) args.push("--args", "-automated");
83
86
  return {
84
87
  command: resolveUnityCliCommand(options),
85
88
  args,
package/src/unity-core.ts CHANGED
@@ -64,8 +64,13 @@ export function buildUnityEditorCandidates(
64
64
  ];
65
65
  }
66
66
 
67
- export function buildUnityOpenEditorArgs(projectRoot: string): string[] {
68
- return ["-projectPath", projectRoot];
67
+ export type UnityOpenEditorArgsOptions = {
68
+ /** Pass Unity Editor's -automated flag. */
69
+ automated?: boolean;
70
+ };
71
+
72
+ export function buildUnityOpenEditorArgs(projectRoot: string, options: UnityOpenEditorArgsOptions = {}): string[] {
73
+ return ["-projectPath", projectRoot, ...(options.automated ? ["-automated"] : [])];
69
74
  }
70
75
 
71
76
  export type UnityBatchmodeArgsOptions = {
@@ -7,6 +7,7 @@ import {
7
7
  buildUnityOpenEditorArgs,
8
8
  normalizeUnityEditorOverride,
9
9
  type SupportedPlatform,
10
+ type UnityOpenEditorArgsOptions,
10
11
  } from "./unity-core";
11
12
  import { createUnityCliOpenCommand, type UnityCliLaunchOptions } from "./unity-cli";
12
13
 
@@ -47,8 +48,12 @@ export async function resolveUnityEditorPath(
47
48
  );
48
49
  }
49
50
 
50
- export function launchUnityEditorDetached(editorPath: string, projectRoot: string): { pid: number | undefined; args: string[]; command: string } {
51
- const args = buildUnityOpenEditorArgs(projectRoot);
51
+ export function launchUnityEditorDetached(
52
+ editorPath: string,
53
+ projectRoot: string,
54
+ options: UnityOpenEditorArgsOptions = {},
55
+ ): { pid: number | undefined; args: string[]; command: string } {
56
+ const args = buildUnityOpenEditorArgs(projectRoot, options);
52
57
  const child = spawn(editorPath, args, {
53
58
  detached: true,
54
59
  stdio: "ignore",
@@ -316,7 +316,7 @@ async function executeCommand(deps: PipelineDependencies, projectRoot: string, c
316
316
  if (deadline !== undefined) ensureBeforeDeadline(deadline, now, command);
317
317
  return result;
318
318
  }
319
- async function requirePreflight(deps: PipelineDependencies, projectRoot: string, unityVersion: string, commands: string[], operation: "recompile" | "tests", signal: AbortSignal | undefined, deadline: number, now: () => number, allowAutonomousExitPlayMode = false): Promise<{ capabilities: UnityCliProjectCapabilities; exitedPlayMode: boolean; playModeHandling: UnityPipelinePlayModeHandling; scriptChangesWhilePlaying?: UnityScriptChangesWhilePlayingPolicy }> {
319
+ async function requirePreflight(deps: PipelineDependencies, projectRoot: string, unityVersion: string, commands: string[], operation: "recompile" | "tests", signal: AbortSignal | undefined, deadline: number, now: () => number, allowAutonomousExitPlayMode = true): Promise<{ capabilities: UnityCliProjectCapabilities; exitedPlayMode: boolean; playModeHandling: UnityPipelinePlayModeHandling; scriptChangesWhilePlaying?: UnityScriptChangesWhilePlayingPolicy }> {
320
320
  const capabilities = await inspectWithDeadline(deps, projectRoot, unityVersion, signal, deadline, now, "preflight");
321
321
  const error = capabilityError(capabilities, commands); if (error) throw new Error(error);
322
322
  let editor = await executeCommand(deps, projectRoot, "editor_status", [], signal, deadline, now);
@@ -335,13 +335,13 @@ async function requirePreflight(deps: PipelineDependencies, projectRoot: string,
335
335
  const policyDescription = policy === "stop_and_recompile"
336
336
  ? "Unity's Script Changes While Playing policy may stop Play Mode to recompile"
337
337
  : "Pipeline editor_status does not expose Unity's Script Changes While Playing policy, so recompilation may continue, defer, or stop Play Mode";
338
- if (!allowAutonomousExitPlayMode) throw new Error(`${policyDescription}; autonomous Play Mode exit is disallowed for this session, so recompile was not started.`);
338
+ if (!allowAutonomousExitPlayMode) throw new Error(`${policyDescription}; Play Mode exit is disabled for this session, so recompile was not started.`);
339
339
  // RecompileCommand owns AssetDatabase.Refresh. Do not preempt it with editor_stop or override Unity's policy.
340
340
  playModeHandling = policy === "stop_and_recompile" ? "unity_policy_may_exit" : "policy_unknown";
341
341
  }
342
342
  } else if (status.lifecycle !== "compatible") {
343
- // Test execution has separate lifecycle semantics: stop explicitly only with session authorization.
344
- if (!allowAutonomousExitPlayMode) throw new Error("Unity Editor is in Play Mode or paused; autonomous Play Mode exit is disallowed for this session, so tests were not started.");
343
+ // Test execution has separate lifecycle semantics: stop explicitly, then verify Edit Mode.
344
+ if (!allowAutonomousExitPlayMode) throw new Error("Unity Editor is in Play Mode or paused; Play Mode exit is disabled for this session, so tests were not started.");
345
345
  const stopError = capabilityError(capabilities, ["editor_stop"]); if (stopError) throw new Error(stopError);
346
346
  const stopped = await executeCommand(deps, projectRoot, "editor_stop", [], signal, deadline, now);
347
347
  if (stopped.error) throw new Error("Unity Pipeline editor_stop failed; tests were not started and Play Mode state is uncertain.");