Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Assets/Tests/Editor/CompileResponseFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,14 @@ public void CreateResponse_WhenForceCompileIsUnknown_ExplainsNullDetails()
CompileResponse response =
CompileResponseFactory.CreateResponse(result, forceRecompile: true);

Assert.That(response.Success, Is.Null);
Assert.That(response.Success, Is.False);
Assert.That(response.ErrorCount, Is.Null);
Assert.That(response.WarningCount, Is.Null);
Assert.That(response.Errors, Is.Null);
Assert.That(response.Warnings, Is.Null);
Assert.That(response.Message, Is.EqualTo(ForceCompileUnknownResult.MessageText));
Assert.That(response.ErrorCode, Is.EqualTo(ForceCompileUnknownResult.ErrorCodeText));
Assert.That(response.NextActions, Is.Not.Empty);
}

[Test]
Expand All @@ -102,12 +104,14 @@ public void CreateResponse_WhenForceCompileHasOutcome_ExplainsNullDetails()
CompileResponse response =
CompileResponseFactory.CreateResponse(result, forceRecompile: true);

Assert.That(response.Success, Is.True);
Assert.That(response.Success, Is.False);
Assert.That(response.ErrorCount, Is.Null);
Assert.That(response.WarningCount, Is.Null);
Assert.That(response.Errors, Is.Null);
Assert.That(response.Warnings, Is.Null);
Assert.That(response.Message, Is.EqualTo(ForceCompileUnknownResult.MessageText));
Assert.That(response.ErrorCode, Is.EqualTo(ForceCompileUnknownResult.ErrorCodeText));
Assert.That(response.NextActions, Is.Not.Empty);
}

[Test]
Expand Down Expand Up @@ -142,7 +146,7 @@ public void CreateResponse_WhenIndeterminateNonForceCompileHasCounts_PreservesCo
CompileResponse response =
CompileResponseFactory.CreateResponse(result, forceRecompile: false);

Assert.That(response.Success, Is.Null);
Assert.That(response.Success, Is.False);
Assert.That(response.ErrorCount, Is.EqualTo(1));
Assert.That(response.WarningCount, Is.EqualTo(1));
Assert.That(response.Errors, Is.Null);
Expand Down
6 changes: 4 additions & 2 deletions Assets/Tests/Editor/CompileStatusBridgeCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public void BuildResponse_WhenUnityIsIdleAndPendingRequestObservedReload_Returns

Assert.That(response.Ready, Is.True);
Assert.That(response.HasResult, Is.True);
Assert.That(response.Result["Success"]?.Type, Is.EqualTo(JTokenType.Null));
Assert.That(response.Result["Success"]?.Value<bool>(), Is.False);
Assert.That(response.Result["ErrorCount"]?.Type, Is.EqualTo(JTokenType.Null));
Assert.That(response.Result["Warnings"]?.Type, Is.EqualTo(JTokenType.Null));
Assert.That(response.Result["Message"]?.ToString(), Does.Contain("reloaded scripts"));
Expand Down Expand Up @@ -216,11 +216,13 @@ public void BuildResponse_WhenForceCompilePendingRequestHasNoResult_ReturnsExpla

Assert.That(response.Ready, Is.True);
Assert.That(response.HasResult, Is.True);
Assert.That(response.Result["Success"]?.Type, Is.EqualTo(JTokenType.Null));
Assert.That(response.Result["Success"]?.Value<bool>(), Is.False);
Assert.That(response.Result["ErrorCount"]?.Type, Is.EqualTo(JTokenType.Null));
Assert.That(
response.Result["Message"]?.ToString(),
Is.EqualTo(ForceCompileUnknownResult.MessageText));
Assert.That(response.Result["ErrorCode"]?.ToString(), Is.EqualTo(ForceCompileUnknownResult.ErrorCodeText));
Assert.That(response.Result["NextActions"]?.Type, Is.EqualTo(JTokenType.Array));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -923,7 +923,6 @@ public void InvalidateCache()

private sealed class SingleFlightTestResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; } = true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ private static string BuildToolRequest(string toolName, int id)

private sealed class DeterministicSuccessResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; } = true;
}

private sealed class DeterministicSuccessTool : IUnityCliLoopTool
Expand Down
1 change: 0 additions & 1 deletion Assets/Tests/Editor/ToolExecutionSessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ public void InvalidateCache()

private sealed class SessionTestResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; } = true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
/// </summary>
public class ClearConsoleResponse : UnityCliLoopToolResponse
{
/// <summary>
/// Whether the console clear operation was successful
/// </summary>
public bool Success { get; set; }

/// <summary>
/// Number of logs that were cleared from the console
/// </summary>
Expand Down Expand Up @@ -101,4 +96,4 @@ public ClearedLogCounts(int errorCount, int warningCount, int logCount)
LogCount = logCount;
}
}
}
}
11 changes: 3 additions & 8 deletions Packages/src/Editor/FirstPartyTools/Compile/CompileResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,6 @@ public CompileIssue() { }
/// </summary>
public class CompileResponse : UnityCliLoopToolResponse
{
/// <summary>
/// Whether compilation was successful.
/// Null is used when the tool cannot reliably determine the result yet (e.g., forced recompile/domain reload),
/// to avoid misleading clients into treating "0 errors" as a confirmed success.
/// </summary>
public bool? Success { get; set; }

/// <summary>
/// Number of compilation errors.
/// Null is used when the tool intentionally does not provide details (e.g., forced recompile),
Expand Down Expand Up @@ -67,6 +60,8 @@ public class CompileResponse : UnityCliLoopToolResponse
/// </summary>
public string Message { get; set; }

public string ErrorCode { get; set; }

/// <summary>
/// Optional recovery steps when compile cannot proceed automatically.
/// </summary>
Expand All @@ -82,7 +77,7 @@ public class CompileResponse : UnityCliLoopToolResponse
/// Create a new CompileResponse
/// </summary>
public CompileResponse(
bool? success,
bool success,
int? errorCount,
int? warningCount,
CompileIssue[] errors,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ internal static CompileResponse CreateResponse(
if (result.IsIndeterminate)
{
return new CompileResponse(
success: result.Success,
success: result.Success == true,
errorCount: result.ErrorCount,
warningCount: result.WarningCount,
errors: null,
Expand All @@ -48,7 +48,7 @@ internal static CompileResponse CreateResponse(
}

CompileResponse response = new CompileResponse(
success: result.Success,
success: result.Success == true,
errorCount: result.Errors?.Length ?? 0,
warningCount: result.Warnings?.Length ?? 0,
errors: ToIssues(result.Errors),
Expand All @@ -75,14 +75,17 @@ private static string[] CreateExternalSceneChangeNextActions(string message)

private static CompileResponse CreateForceCompileResult(CompileResult result)
{
ForceCompileUnknownResult unknownResult = ForceCompileUnknownResult.Create(result.Success);
return new CompileResponse(
ForceCompileUnknownResult unknownResult = ForceCompileUnknownResult.Create();
CompileResponse response = new CompileResponse(
success: unknownResult.Success,
errorCount: unknownResult.ErrorCount,
warningCount: unknownResult.WarningCount,
errors: null,
warnings: null,
message: unknownResult.Message);
response.ErrorCode = ForceCompileUnknownResult.ErrorCodeText;
response.NextActions = new[] { ForceCompileUnknownResult.NextActionText };
return response;
}

private static CompileIssue[] ToIssues(UnityEditor.Compilation.CompilerMessage[] messages)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
/// </summary>
public class ExecuteDynamicCodeResponse : UnityCliLoopToolResponse, IUnityCliLoopTimingResponse
{
/// <summary>Execution success flag</summary>
public bool Success { get; set; }

/// <summary>Execution result</summary>
public string Result { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ public class ClearPausePointSchema : UnityCliLoopToolSchema
/// </summary>
public class PausePointResponse : UnityCliLoopToolResponse
{
// Defaults to true so existing snapshot/clear paths keep their prior semantics.
// Only explicit validation failures set this to false.
public bool Success { get; set; } = true;
public string Id { get; set; } = string.Empty;
public int ResolvedLine { get; set; }
public string ResolvedMethod { get; set; } = string.Empty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
/// </summary>
public class RaycastResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; }
public string Message { get; set; } = "";
public bool Hit { get; set; }
public string? HitGameObjectName { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
/// </summary>
public class RecordInputResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; }
public string Message { get; set; } = "";
public string Action { get; set; } = "";
public string? OutputPath { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
/// </summary>
public class ReplayInputResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; }
public string Message { get; set; } = "";
public string Action { get; set; } = "";
public string? InputPath { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,6 @@ public class RunTestsResponse : UnityCliLoopToolResponse
public static readonly string TestFrameworkUnavailableMessage =
$"run-tests requires the Unity Test Framework package ({UnityCliLoopConstants.PACKAGE_NAME_TEST_FRAMEWORK}). Install it via Package Manager to use test execution.";

/// <summary>
/// Whether test execution was successful
/// </summary>
public bool Success { get; set; }

/// <summary>
/// Machine-readable execution status
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ private async Task<ScreenshotResponse> CaptureRenderingAsync(
"CaptureMode.rendering requires PlayMode",
correlationId: correlationId
);
return new ScreenshotResponse();
return new ScreenshotResponse
{
Success = false,
Message = "Rendering screenshots require PlayMode, but Unity is currently in EditMode.",
NextActions = new[] { "Start PlayMode with `uloop control-play-mode --action Play`, then retry the rendering screenshot." }
};
}

List<UIElementInfo> annotatedElements = new();
Expand Down Expand Up @@ -193,7 +198,12 @@ private async Task<ScreenshotResponse> CaptureRenderingAsync(
"Play Mode view RenderTexture is not available. Open the Game view or Device Simulator and wait for a frame before retrying.",
correlationId: correlationId
);
return new ScreenshotResponse();
return new ScreenshotResponse
{
Success = false,
Message = "PlayMode rendering did not produce an image.",
NextActions = new[] { "Open the Game view or Device Simulator, wait for a frame, then retry the rendering screenshot." }
};
}

int width = texture.width;
Expand Down Expand Up @@ -296,7 +306,9 @@ private async Task<ScreenshotResponse> CaptureWindowsAsync(
);
return new ScreenshotResponse
{
Success = false,
Message = notFoundMessage,
NextActions = new[] { "Open the requested Unity window, then retry the screenshot." }
};
}

Expand Down Expand Up @@ -399,8 +411,10 @@ internal static ScreenshotResponse CreateTimedOutResult(

return new ScreenshotResponse
{
Success = false,
TimedOut = true,
Message = message,
NextActions = new[] { "Retry the screenshot after Unity finishes rendering the requested frame." },
Screenshots = screenshots,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
/// </summary>
public class SimulateKeyboardResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; }
public string Message { get; set; } = "";
public string Action { get; set; } = "";
public string? KeyName { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
/// </summary>
public class SimulateMouseInputResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; }
public string Message { get; set; } = "";
public string Action { get; set; } = "";
public string? Button { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
/// </summary>
public class SimulateMouseUiResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; }
public string Message { get; set; } = "";
public string Action { get; set; } = "";
public string? HitGameObjectName { get; set; }
Expand Down
1 change: 0 additions & 1 deletion Packages/src/Editor/FirstPartyTools/Watch/WatchTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ public sealed class GetWatchValuesSchema : UnityCliLoopToolSchema
/// </summary>
public sealed class WatchResponse : UnityCliLoopToolResponse
{
public bool Success { get; set; } = true;
public string Id { get; set; } = string.Empty;
public string Expression { get; set; } = string.Empty;
public int MaxHistory { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,18 +164,20 @@ private static JObject CreateRecoveredCompileResult(
if (pendingRequest.ForceRecompile)
{
ForceCompileUnknownResult unknownForceCompileResult =
ForceCompileUnknownResult.Create(null);
ForceCompileUnknownResult.Create();
message = unknownForceCompileResult.Message;
}

return new JObject
{
["Success"] = JValue.CreateNull(),
["Success"] = false,
["ErrorCount"] = JValue.CreateNull(),
["WarningCount"] = JValue.CreateNull(),
["Errors"] = JValue.CreateNull(),
["Warnings"] = JValue.CreateNull(),
["Message"] = message,
["ErrorCode"] = ForceCompileUnknownResult.ErrorCodeText,
["NextActions"] = new JArray(ForceCompileUnknownResult.NextActionText),
["ProjectRoot"] = UnityCliLoopPathResolver.GetProjectRoot()
};
}
Expand Down
14 changes: 8 additions & 6 deletions Packages/src/Editor/ToolContracts/ForceCompileUnknownResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,26 @@ namespace io.github.hatayama.UnityCliLoop.ToolContracts
/// </summary>
public sealed class ForceCompileUnknownResult
{
public const string MessageText = "Forced full compilation completed. Unity does not return a definitive compile result for this forced full compile path, so fields that Unity did not provide are intentionally null; run get-logs to inspect the compiler output.";
public const string MessageText = "Forced full compilation was triggered, but Unity did not provide a definitive result after domain reload.";
public const string ErrorCodeText = "COMPILE_RESULT_UNKNOWN";
public const string NextActionText = "Wait for domain reload to complete, then run `uloop compile` without --force-recompile to obtain a definitive result.";

private ForceCompileUnknownResult(bool? success)
private ForceCompileUnknownResult()
{
Success = success;
Success = false;
ErrorCount = null;
WarningCount = null;
Message = MessageText;
}

public bool? Success { get; }
public bool Success { get; }
public int? ErrorCount { get; }
public int? WarningCount { get; }
public string Message { get; }

public static ForceCompileUnknownResult Create(bool? success)
public static ForceCompileUnknownResult Create()
{
return new ForceCompileUnknownResult(success);
return new ForceCompileUnknownResult();
}
}
}
1 change: 1 addition & 0 deletions Packages/src/Editor/ToolContracts/ScreenshotResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class ScreenshotResponse : UnityCliLoopToolResponse
public List<ScreenshotInfo> Screenshots { get; set; } = new List<ScreenshotInfo>();
public bool TimedOut { get; set; }
public string Message { get; set; } = "";
public string[] NextActions { get; set; } = new string[0];

public int ScreenshotCount => Screenshots.Count;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace io.github.hatayama.UnityCliLoop.ToolContracts
/// </summary>
public abstract class UnityCliLoopToolResponse
{
public bool Success { get; set; } = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if the protocol version was incremented in the contract and constants files.

# Check contract.json
echo "--- cli/common/clicontract/contract.json ---"
cat cli/common/clicontract/contract.json | grep -i "protocolVersion" || echo "File or field not found."

# Check CliConstants.cs
echo "--- CliConstants.cs ---"
rg -i "REQUIRED_CLI_PROTOCOL_VERSION" Packages/src/ || echo "Constant not found."

Repository: hatayama/unity-cli-loop

Length of output: 976


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- Packages/src/Editor/ToolContracts/UnityCliLoopToolResponse.cs ---"
sed -n '1,120p' Packages/src/Editor/ToolContracts/UnityCliLoopToolResponse.cs

echo
echo "--- Packages/src/Editor/Infrastructure/Api/JsonRpcResponseFactory.cs ---"
sed -n '1,220p' Packages/src/Editor/Infrastructure/Api/JsonRpcResponseFactory.cs

echo
echo "--- Packages/src/Editor/Infrastructure/Api/JsonRpcRequestProcessor.cs ---"
sed -n '1,220p' Packages/src/Editor/Infrastructure/Api/JsonRpcRequestProcessor.cs

Repository: hatayama/unity-cli-loop

Length of output: 17941


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- search for JsonRpcResponseSerializer and Success fields ---"
rg -n "JsonRpcResponseSerializer|Success\s*\{" Packages/src/ -g'*.cs'

echo
echo "--- Packages/src/Editor/Infrastructure/Api/JsonRpcResponseSerializer.cs ---"
sed -n '1,220p' Packages/src/Editor/Infrastructure/Api/JsonRpcResponseSerializer.cs

echo
echo "--- response model files containing JsonRpcSuccessResponse ---"
rg -n "class JsonRpcSuccessResponse|record JsonRpcSuccessResponse|JsonRpcSuccessResponse" Packages/src/Editor/Infrastructure/Api/ -g'*.cs'

Repository: hatayama/unity-cli-loop

Length of output: 3865


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- search serializer settings / strict JSON handling ---"
rg -n "MissingMemberHandling|DefaultValueHandling|NullValueHandling|Required\\.|JsonProperty\\(|JsonObject\\(|JsonExtensionData|AllowTrailingCommas|MetadataPropertyHandling" Packages/src/ -g'*.cs'

echo
echo "--- UnityCliLoopJsonResponseSerializerSettings.cs ---"
sed -n '1,240p' Packages/src/Editor/Infrastructure/Api/UnityCliLoopJsonResponseSerializerSettings.cs

echo
echo "--- JsonRpcSuccessResponse.cs ---"
sed -n '1,200p' Packages/src/Editor/Infrastructure/Api/JsonRpcSuccessResponse.cs

Repository: hatayama/unity-cli-loop

Length of output: 2669


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- search for Success deserialization on the CLI/package side ---"
rg -n "\bSuccess\b" Packages/src/ cli/ -g'*.cs' -g'*.json'

echo
echo "--- search for strict contract assertions around response payloads ---"
rg -n "fail closed|missing Success|Success value|success field|required.*Success|deserialize.*Success" Packages/src/ cli/ -g'*.cs' -g'*.json'

Repository: hatayama/unity-cli-loop

Length of output: 36984


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- locate serializer settings file ---"
fd -a "UnityCliLoopJsonResponseSerializerSettings.cs" Packages/src/ || true
fd -a "*JsonResponseSerializerSettings*.cs" Packages/src/ || true

echo
echo "--- read serializer settings candidates ---"
for f in $(fd -a "UnityCliLoopJsonResponseSerializerSettings.cs" Packages/src/; fd -a "*JsonResponseSerializerSettings*.cs" Packages/src/); do
  echo "### $f"
  sed -n '1,220p' "$f"
  echo
done

echo "--- JsonRpcSuccessResponse.cs ---"
sed -n '1,200p' Packages/src/Editor/Infrastructure/Api/JsonRpcSuccessResponse.cs

Repository: hatayama/unity-cli-loop

Length of output: 2636


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- changed files touching protocol surfaces ---"
git diff --stat -- cli/common/clicontract/contract.json Packages/src/Editor/Domain/CliConstants.cs Packages/src/Editor/ToolContracts/UnityCliLoopToolResponse.cs

echo
echo "--- contract.json diff ---"
git diff --unified=0 -- cli/common/clicontract/contract.json

echo
echo "--- CliConstants.cs diff ---"
git diff --unified=0 -- Packages/src/Editor/Domain/CliConstants.cs

echo
echo "--- UnityCliLoopToolResponse.cs diff ---"
git diff --unified=0 -- Packages/src/Editor/ToolContracts/UnityCliLoopToolResponse.cs

Repository: hatayama/unity-cli-loop

Length of output: 310


Bump the CLI protocol version. Success is now part of the response contract, so older Unity packages and the new CLI won’t interoperate unless cli/common/clicontract/contract.json and Packages/src/Editor/Domain/CliConstants.cs move together.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Editor/ToolContracts/UnityCliLoopToolResponse.cs` at line 8,
Update the CLI protocol version consistently in
cli/common/clicontract/contract.json and the corresponding version constant in
CliConstants, reflecting that UnityCliLoopToolResponse.Success is now part of
the response contract. Ensure both values are incremented together so older
Unity packages and the new CLI are not treated as compatible.

Source: Path instructions

}

public interface IUnityCliLoopTimingResponse
Expand Down
2 changes: 1 addition & 1 deletion cli/dispatcher/internal/dispatcher/launch_ready.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const (
launchReadyMessage = "Unity CLI Loop is ready."
launchAlreadyRunningReadyMessage = "Unity is already running and ready."
launchStoppedMessage = "Unity process stopped."
launchNoProcessMessage = "No Unity process is running for this project."
launchNoProcessMessage = "No matching Unity process was found; it may have already exited."
)

type launchReadyResponse struct {
Expand Down
Loading
Loading