From 1a6d393c3a5335adedd56dd79f9e83cab5bd43af Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 26 Jul 2026 01:01:36 +0900 Subject: [PATCH 1/5] Remove leftover execute-dynamic-code readiness probe helpers These three symbols had zero callers after readiness probing moved to get-version in 7e45f1e7. Delete them rather than rewire the old path, which would let user-disabled tools block Editor startup again. Co-authored-by: Cursor --- .../DynamicCodeForegroundWarmupRunner.cs | 24 ------------------- .../ExecuteDynamicCodeReadinessProbe.cs | 5 ---- .../FirstPartyToolsEditorStartup.cs | 7 ------ 3 files changed, 36 deletions(-) diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/DynamicCodeForegroundWarmupRunner.cs b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/DynamicCodeForegroundWarmupRunner.cs index 6ae35352a..53f711efb 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/DynamicCodeForegroundWarmupRunner.cs +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/DynamicCodeForegroundWarmupRunner.cs @@ -32,30 +32,6 @@ internal static async Task RunForegroundSequenceAsync( return true; } - internal static async Task TryRunBackgroundSequenceAsync( - IDynamicCodeExecutionRuntime runtime, - bool yieldToForegroundRequests, - CancellationToken ct) - { - System.Diagnostics.Debug.Assert(runtime != null, "runtime must not be null"); - - // Why: background probes must match the foreground sequence so whichever path succeeds - // first marks the same execution shape as ready. - foreach (string warmupCode in ExecuteDynamicCodeReadinessProbe.CreateReturnStringProbeCodes()) - { - DynamicCodeExecutionRequest request = CreateRequest( - warmupCode, - yieldToForegroundRequests); - (bool entered, ExecutionResult result) = await runtime.TryExecuteIfIdleAsync(request, ct).ConfigureAwait(false); - if (!entered || !result.Success) - { - return false; - } - } - - return true; - } - private static DynamicCodeExecutionRequest CreateRequest( string code, bool yieldToForegroundRequests) diff --git a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/ExecuteDynamicCodeReadinessProbe.cs b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/ExecuteDynamicCodeReadinessProbe.cs index 1649dafd7..224772392 100644 --- a/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/ExecuteDynamicCodeReadinessProbe.cs +++ b/Packages/src/Editor/FirstPartyTools/ExecuteDynamicCode/Execution/ExecuteDynamicCodeReadinessProbe.cs @@ -4,11 +4,6 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools // otherwise one path can look ready while the user's first return-string execution is still cold. public static class ExecuteDynamicCodeReadinessProbe { - public static string CreatePrimaryReturnStringProbeCode() - { - return DynamicCodeForegroundWarmupSnippets.ReturnStringShapes[0]; - } - public static string[] CreateReturnStringProbeCodes() { string[] source = DynamicCodeForegroundWarmupSnippets.ReturnStringShapes; diff --git a/Packages/src/Editor/FirstPartyTools/FirstPartyToolsEditorStartup.cs b/Packages/src/Editor/FirstPartyTools/FirstPartyToolsEditorStartup.cs index 549fa3457..1054d0309 100644 --- a/Packages/src/Editor/FirstPartyTools/FirstPartyToolsEditorStartup.cs +++ b/Packages/src/Editor/FirstPartyTools/FirstPartyToolsEditorStartup.cs @@ -36,12 +36,5 @@ public static void ResetServerScopedServicesBeforeDomainReload() { ExecuteDynamicCodeEditorStartup.ResetServerScopedServicesBeforeDomainReload(); } - - public static string CreateExecuteDynamicCodeReadinessProbeCode() - { - // Why: composition root can only depend on the bundled-tool facade assembly, - // so the dynamic-code assembly keeps ownership of the actual probe source shape. - return ExecuteDynamicCodeReadinessProbe.CreatePrimaryReturnStringProbeCode(); - } } } From e6ed30426832d2697d6a1fdc22ff766e2934ee1f Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 26 Jul 2026 01:03:32 +0900 Subject: [PATCH 2/5] Add CI gate that fails on high-confidence dead code The scanner already existed but was never wired into GitHub Actions, so unused C# symbols could land unnoticed. Run it on package and scanner path changes with --fail-on high-confidence only. Co-authored-by: Cursor --- .github/workflows/dead-code.yml | 45 +++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/dead-code.yml diff --git a/.github/workflows/dead-code.yml b/.github/workflows/dead-code.yml new file mode 100644 index 000000000..64803e179 --- /dev/null +++ b/.github/workflows/dead-code.yml @@ -0,0 +1,45 @@ +name: Dead Code + +on: + pull_request: + branches: [ main, v3-beta ] + paths: + - 'Packages/src/**/*.cs' + - 'tools/UnityCliLoop.DeadCodeScanner/**' + - 'tests/UnityCliLoop.DeadCodeScanner.Tests/**' + - 'scripts/check-dead-code.sh' + - '.github/workflows/dead-code.yml' + workflow_dispatch: + +permissions: + contents: read + +jobs: + dead-code: + name: Dead Code Gate + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + + - name: Setup .NET + uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 + with: + dotnet-version: 10.0.x + + - name: Test dead code scanner + run: dotnet test tests/UnityCliLoop.DeadCodeScanner.Tests/UnityCliLoop.DeadCodeScanner.Tests.csproj --configuration Release + + - name: Scan for dead code + run: > + scripts/check-dead-code.sh + --root . + --scope public + --include-types true + --include-members true + --include-locals true + --include-test-only true + --include-kept false + --format table + --fail-on high-confidence From fa77edc41583bd13ef05fa1d32320fa5bdfdb7e4 Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 26 Jul 2026 01:03:33 +0900 Subject: [PATCH 3/5] Document the dead-code scanner CI gate Contributors need to know which categories fail CI and which still need manual review of non-C# references. Co-authored-by: Cursor --- docs/dead-code-scanner.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/dead-code-scanner.md b/docs/dead-code-scanner.md index 2a146fd7d..3b23d4c32 100644 --- a/docs/dead-code-scanner.md +++ b/docs/dead-code-scanner.md @@ -17,6 +17,18 @@ For a broader member/local-variable pass, run: dotnet run --project tools/UnityCliLoop.DeadCodeScanner -- --scope public --include-types true --include-members true --include-locals true --include-test-only true --include-kept false --format table ``` +## CI gate + +`.github/workflows/dead-code.yml` runs automatically on pull requests that +touch `Packages/src/**/*.cs`, the scanner itself, its tests, or +`scripts/check-dead-code.sh`. + +The gate uses `--fail-on high-confidence`, so CI fails only for +`Unused`, `UnusedPrivateMember`, and `UnusedLocal`. + +`PublicCandidate` and `TestOnly` do not fail CI. Those findings need +manual review of non-C# references and cannot be decided mechanically. + ## Interpreting the output Interpret scanner output conservatively: From 1ad73872e25d915bfbff37714c59f1f04c0c2c27 Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 26 Jul 2026 01:14:32 +0900 Subject: [PATCH 4/5] Harden the dead-code workflow checkout and document its trigger scope CodeRabbit flagged credential persistence on a PR-triggered workflow that runs repository scripts, and the docs omitted the base-branch filter and self-path trigger that the YAML already enforces. Co-authored-by: Cursor --- .github/workflows/dead-code.yml | 2 ++ docs/dead-code-scanner.md | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dead-code.yml b/.github/workflows/dead-code.yml index 64803e179..c25e83ad4 100644 --- a/.github/workflows/dead-code.yml +++ b/.github/workflows/dead-code.yml @@ -22,6 +22,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + persist-credentials: false - name: Setup .NET uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 diff --git a/docs/dead-code-scanner.md b/docs/dead-code-scanner.md index 3b23d4c32..200960273 100644 --- a/docs/dead-code-scanner.md +++ b/docs/dead-code-scanner.md @@ -20,8 +20,8 @@ dotnet run --project tools/UnityCliLoop.DeadCodeScanner -- --scope public --incl ## CI gate `.github/workflows/dead-code.yml` runs automatically on pull requests that -touch `Packages/src/**/*.cs`, the scanner itself, its tests, or -`scripts/check-dead-code.sh`. +target `main` or `v3-beta` and touch `Packages/src/**/*.cs`, the scanner +itself, its tests, `scripts/check-dead-code.sh`, or the workflow file. The gate uses `--fail-on high-confidence`, so CI fails only for `Unused`, `UnusedPrivateMember`, and `UnusedLocal`. From f29589f25941b84e06e92a221611a21f032ceb0c Mon Sep 17 00:00:00 2001 From: hatayama Date: Sun, 26 Jul 2026 01:24:00 +0900 Subject: [PATCH 5/5] Cover the dead-code gate's failing path in unit tests The CI gate only fails when --fail-on high-confidence is parsed and findings are classified as high-confidence candidates. Without tests on either side, a broken parse or inverted category check would stay green forever while the gate looked effective. Co-authored-by: Cursor --- .../CommandLineOptionsTests.cs | 28 +++++++++++++++++++ .../DeadCodeScannerTests.cs | 13 +++++++++ 2 files changed, 41 insertions(+) create mode 100644 tests/UnityCliLoop.DeadCodeScanner.Tests/CommandLineOptionsTests.cs diff --git a/tests/UnityCliLoop.DeadCodeScanner.Tests/CommandLineOptionsTests.cs b/tests/UnityCliLoop.DeadCodeScanner.Tests/CommandLineOptionsTests.cs new file mode 100644 index 000000000..23b21d925 --- /dev/null +++ b/tests/UnityCliLoop.DeadCodeScanner.Tests/CommandLineOptionsTests.cs @@ -0,0 +1,28 @@ +using NUnit.Framework; +using UnityCliLoop.DeadCodeScanner; + +namespace UnityCliLoop.DeadCodeScanner.Tests +{ + [TestFixture] + public sealed class CommandLineOptionsTests + { + // Verifies that --fail-on high-confidence enables the CI fail flag and --fail-on none leaves it off. + [Test] + public void Parse_WhenFailOnIsHighConfidenceOrNone_ShouldSetFailOnHighConfidenceFlag() + { + ScanOptions highConfidenceOptions = CommandLineOptions.Parse(new[] + { + "--fail-on", + "high-confidence" + }); + ScanOptions noneOptions = CommandLineOptions.Parse(new[] + { + "--fail-on", + "none" + }); + + Assert.That(highConfidenceOptions.FailOnHighConfidence, Is.True); + Assert.That(noneOptions.FailOnHighConfidence, Is.False); + } + } +} diff --git a/tests/UnityCliLoop.DeadCodeScanner.Tests/DeadCodeScannerTests.cs b/tests/UnityCliLoop.DeadCodeScanner.Tests/DeadCodeScannerTests.cs index b9f14cd46..67750a1a5 100644 --- a/tests/UnityCliLoop.DeadCodeScanner.Tests/DeadCodeScannerTests.cs +++ b/tests/UnityCliLoop.DeadCodeScanner.Tests/DeadCodeScannerTests.cs @@ -107,6 +107,19 @@ public async Task ScanAsync_WhenProductionSymbolIsOnlyUsedByAssets_ShouldReportT && issue.FullName.Contains("TestOnlyFactory", StringComparison.Ordinal)), Is.True); } + // Verifies that default-scope unused private findings are treated as high-confidence deletion candidates for the CI gate. + [Test] + public async Task ScanAsync_WhenUsingDefaultPrivateScope_ShouldReportHighConfidenceDeletionCandidates() + { + DeadCodeScanner scanner = new(); + ScanOptions options = ScanOptions.Default(_rootPath); + + System.Collections.Generic.IReadOnlyList issues = + await scanner.ScanAsync(options, CancellationToken.None); + + Assert.That(issues.Any(issue => issue.IsHighConfidenceDeletionCandidate()), Is.True); + } + // Verifies that Unity or reflection entry points can be reported separately when requested. [Test] public async Task ScanAsync_WhenIncludingKeptSymbols_ShouldReportUnityToolAsKept()