Sentinel

v1.0.0

Project Health & Integrity for Unity — find issues, clean garbage, track references, and guard your builds.

Getting Started

Requirements

Installation

After importing from the Asset Store, Sentinel lives at Assets/Sentinel/. Unity will compile the YourStudio.Sentinel.Editor assembly automatically. No setup required.

Opening Sentinel

Go to Window > Sentinel > Open Window or press Ctrl+Shift+Alt+S.

The main window has a toolbar and four tabs: Health, Issues, Unused Assets, and Dependencies.

Your First Scan

  1. Open the Sentinel window
  2. Click "Scan Full Project" in the toolbar
  3. Wait for the progress bar to finish
  4. Review the results in the Issues tab
Tip: Use "Quick Scan" for a faster scan that only checks for missing scripts, missing references, and missing materials — the most critical issues.

Issues Finder

The Issues tab scans your entire project — including closed scenes (via YAML parsing, without opening them) — and reports problems organized by severity.

Built-in Detectors

DetectorDefault SeverityWhat It Finds
Missing ScriptErrorGameObjects with deleted/renamed MonoBehaviour scripts
Missing ReferenceErrorSerialized fields pointing to destroyed/missing assets
Missing MaterialErrorRenderers with null material slots
Shader ErrorErrorShaders that fail to compile
Empty UnityEventWarningUnityEvent fields with zero persistent listeners
Invalid LayerWarningGameObjects assigned to undefined layer indices
Invalid TagWarningGameObjects with tags that don't exist in the Tag Manager
Unused ComponentWarningLone MonoBehaviours on childless leaf GameObjects
Empty GameObjectInfoGameObjects with only a Transform and no children
Duplicate NameInfoMultiple assets sharing the same filename

Columns

ColumnDescription
TypeThe issue detector name (e.g., MissingScript, MissingRef)
Asset PathFull project path to the asset (e.g., Assets/Scenes/Level1.unity)
Object TypeThe Unity asset type (e.g., SceneAsset, GameObject, MonoScript)
MessageHuman-readable description of the issue

Click any column header to sort by that column. Click again to reverse the sort order.

Filtering

The left panel provides filters:

Fixing Issues

Issues that have automatic fixes show a "Fix:" option when right-clicked:

All fixes support Undo (Ctrl+Z). After any fix is applied, Sentinel automatically rescans the project and shows a popup confirming how many issues were resolved.

The "Fix All Auto-Fixable" button in the left panel applies the first available fix for every fixable issue at once.

Suppression

Right-click any issue and select "Suppress Issue" to hide it from future scans. Suppressions are saved to Assets/SentinelData/suppressions.json (version-controlled, shared with your team).

To remove a suppression, enable the "Suppressed" filter, find the issue, right-click, and select "Remove Suppression."

Context Menu

Right-click any issue row for:

Clicking any row pings the asset in the Project window.

Project Cleaner

The Unused Assets tab finds unreferenced assets — files that are not reachable from any build scene, Resources folder, or other root — and are safe to delete.

How It Works

Sentinel uses a root-tracing algorithm:

  1. Identifies all root assets that must be kept (build scenes, Resources, StreamingAssets, scripts, Editor folders, Plugins, asset bundles, Addressables)
  2. Recursively traces every dependency from each root using AssetDatabase.GetDependencies
  3. Any asset in your project NOT reached by this trace is flagged as garbage

Protected Assets (Never Flagged)

Using the Cleaner

  1. Switch to the Unused Assets tab
  2. Click "Find Deletable Assets"
  3. Review the results grouped by folder
  4. Use checkboxes to select/deselect individual assets
  5. Click "Delete Selected"

Delete Confirmation

When you click "Delete Selected," a confirmation dialog appears with two options:

Move to Trash (default, reversible)

Assets are moved to Assets/SentinelTrash/ where you can review them. To restore, simply move them back. To permanently delete later, remove the folder manually.

Delete Permanently

Assets are deleted from the project. A backup ZIP is automatically created at Library/SentinelBackups/ before deletion.

Important: Always save your scene and project before deleting assets. While Sentinel protects critical assets, review the list carefully before confirming.

Full Preview Window

Click "Open Full Preview Window" for a larger, dedicated window with:

References Finder

The Dependencies tab answers: "Where is this asset used?"

How to Use

  1. Switch to the Dependencies tab
  2. Drag an asset into the Target Asset field (or use the object picker)
  3. Click "Find References"
  4. The tree view shows every asset that references your target

Sentinel rebuilds the dependency map fresh each time you search, so it always reflects the current project state. It also performs a supplementary YAML search across all scene and prefab files to catch references that the map might miss.

Results

Tip: Make sure your scenes are saved before searching. Unsaved scene changes won't appear in the results until the scene file is written to disk.

The Health tab tracks your project's health over time.

Health Score

A score from 0 to 100 calculated after each scan:

Score = 100 - (Errors × 5) - (Warnings × 1) - (Infos × 0.2)    clamped to [0, 100]

80–100: Healthy

Project is in good shape. Few or no critical issues.

50–79: Needs Attention

Some issues accumulating. Schedule cleanup time.

0–49: Critical

Significant issues. Address errors before shipping.

Delta Indicator

Shows whether issues increased or decreased since the previous scan.

Trends Chart

After 2+ scans, a line chart shows errors (red), warnings (yellow), and total issues (white) over time. Sentinel stores up to 60 scan snapshots.

Baseline

Lock a baseline to establish "known issues." Future scans will report how many new issues appeared since the baseline was locked. This is especially useful for CI pipelines: allow existing tech debt but block new regressions.

Settings

Go to Edit > Project Settings > Sentinel to configure.

Scan Scope

Severity Configuration

Override the default severity for each issue type. For example, change "Empty UnityEvent" from Warning to Info if your project uses event-driven patterns where empty events are intentional.

Scan Options

OptionDefaultDescription
Scan Open ScenesOnInclude currently open scenes in the scan
Scan Closed ScenesOnScan .unity files via YAML (without opening them)
Auto Scan on CompileOffTrigger a scan after every script compilation
Fail Build on ErrorsOffBlock player builds if Error-severity issues exist

Notifications

Paste a Slack or Discord webhook URL to receive scan result notifications. Click "Test Webhook" to verify.

Build Pre-Check

When "Fail Build on Errors" is enabled, Sentinel runs a fast pre-build scan (Missing Scripts + Missing References only) before every player build. If errors are found, the build is blocked with a summary of issues.

Suppression via Code

Add the [SentinelIgnore] attribute to fields, properties, or entire classes to suppress issue detection:

[SentinelIgnore("Intentionally null during async loading")]
public GameObject optionalReference;

Project Window Context Menu

Right-click assets or folders in the Project window:

CI/CD Integration

Command-Line Usage

Run Sentinel in headless mode for CI pipelines:

Unity.exe -batchmode -quit -projectPath /path/to/project \
  -executeMethod YourStudio.Sentinel.Editor.SentinelCLI.RunScan \
  -sentinelOutput Reports/sentinel-report.json \
  -sentinelFailOn Error

CLI Arguments

ArgumentDefaultDescription
-sentinelOutputsentinel-report.jsonOutput path for the JSON report
-sentinelFailOnErrorMinimum severity to fail (Error, Warning, Info)

Exit Codes

CodeMeaning
0Scan passed — no issues at or above the fail threshold
1Scan failed — issues found at or above the fail threshold
2Scan error — crash, missing profile, or configuration problem

Output Reports

Two reports are generated side by side:

JSON Report

Machine-readable. Contains scan date, Unity version, duration, issue summary (errors/warnings/infos/suppressed), new-since-baseline count, and the full issues array.

JUnit XML Report

Standard test result format. Compatible with GitHub Actions (dorny/test-reporter), Jenkins, GitLab CI. Includes file attributes for inline PR annotations and a <properties> block with scan metadata.

GitHub Actions Template

A ready-to-use workflow is included at Assets/Sentinel/CI/sentinel-ci.yml. Copy it to .github/workflows/sentinel-ci.yml in your repository. It uses Game CI (game-ci/unity-builder@v4).

Lock Baseline from CLI

Unity.exe -batchmode -quit -projectPath /path/to/project \
  -executeMethod YourStudio.Sentinel.Editor.SentinelCLI.LockBaseline

Git Post-Merge Hook

Sentinel can install a git hook that automatically runs a health scan after every git pull / git merge. Call SentinelGitHook.InstallPostMergeHook() from code or a menu item.

Custom Extensions

Sentinel's detector system is fully extensible. Create your own detectors to enforce project-specific rules — naming conventions, component limits, folder structure, and more. Custom extensions are auto-discovered via TypeCache — no registration, no config files.

Creating a Custom Detector

  1. Create a new C# script in an Editor folder
  2. Add an assembly definition (.asmdef) that references YourStudio.Sentinel.Editor and targets the Editor platform only
  3. Create a class that extends SentinelExtensionBase
  4. Override DetectorName and whichever lifecycle hooks you need
  5. Call AddIssue() to report problems

SentinelExtensionBase API

MemberDescription
DetectorName requiredUnique string identifying your detector (e.g., "NamingConvention")
OnScanStart(ctx)Called once when a scan begins. Use it to load settings or initialize state.
OnAssetScanned(assetPath, so)Called for every asset in the project. so is the SerializedObject (may be null for non-serializable assets).
OnPrefabScanned(prefabPath, root)Called for each prefab with its root GameObject.
OnSceneScanned(scenePath, rootObjects)Called for each open scene with its root GameObject[] array.
OnSceneTextScanned(scenePath, sceneText)Called for each closed scene with its raw scene-file text (YAML) as a string.
OnScanEnd(ctx)Called once after all assets have been scanned. Use for project-wide checks.
AddIssue(record)Reports an issue. Sentinel assigns a unique ID automatically.

Minimal Example

using System.IO;
using UnityEditor;

namespace YourStudio.Sentinel.Editor
{
    public class NamingConventionDetector : SentinelExtensionBase
    {
        public override string DetectorName => "NamingConvention";

        private SentinelSeverityConfig _severityConfig;

        public override void OnScanStart(SentinelScanContext ctx)
        {
            _severityConfig = SentinelProjectSettings.GetOrCreate().SeverityConfig;
        }

        public override void OnAssetScanned(string assetPath, SerializedObject so)
        {
            string fileName = Path.GetFileNameWithoutExtension(assetPath);

            // Prefabs should use PascalCase and have no spaces
            if (assetPath.EndsWith(".prefab") && fileName.Contains(" "))
            {
                AddIssue(new SentinelIssueRecord
                {
                    Severity = _severityConfig.GetSeverity("NamingConvention"),
                    IssueType = "NamingConvention",
                    AssetPath = assetPath,
                    Message = $"Prefab '{fileName}' contains spaces"
                });
            }
        }
    }
}

Assembly Definition Setup

Your custom extension needs its own .asmdef file to reference the Sentinel assembly:

{
    "name": "MyProject.SentinelExtensions",
    "rootNamespace": "",
    "references": [ "YourStudio.Sentinel.Editor" ],
    "includePlatforms": [ "Editor" ],
    "excludePlatforms": [],
    "allowUnsafeCode": false,
    "overrideReferences": false,
    "precompiledReferences": [],
    "autoReferenced": true,
    "defineConstraints": [],
    "versionDefines": [],
    "noEngineReferences": false
}
Tip: Place your extension in its own folder with its own .asmdef. This keeps it separate from Sentinel internals and makes upgrades easier.

SentinelIssueRecord Fields

FieldTypeDescription
SeveritySentinelIssueSeverityError, Warning, or Info. Use SeverityConfig.GetSeverity() to let users override.
IssueTypestringMust match your DetectorName.
AssetPathstringFull asset path (e.g., Assets/Prefabs/Player.prefab).
ObjectPathstring(Optional) Path within the asset (e.g., GameObject name).
MessagestringHuman-readable description shown in the Issues tab.

Included Examples (SentinelExtensionDemo)

Sentinel ships with three example detectors in Assets/Sentinel/SentinelExtensionDemo/. These serve as both documentation and real, functional detectors you can modify or use as templates:

NamingConventionDetector

Uses OnAssetScanned to enforce naming rules: no spaces in prefab names, M_/Mat_ prefix for materials, T_ prefix for textures. Demonstrates how to use SeverityConfig.GetSeverity() so users can override severity per-project.

LargeComponentCountDetector

Uses OnPrefabScanned to flag prefabs exceeding a component threshold (default: 10). Demonstrates the prefab lifecycle hook and GetComponentsInChildren traversal.

EmptyFolderDetector

Uses OnScanEnd to recursively scan for empty folders project-wide. Demonstrates the end-of-scan hook for checks that don't map to individual assets.

SentinelIgnore Attribute

The [SentinelIgnore] attribute can be applied to fields, properties, or entire classes to suppress issue detection for specific references:

using UnityEngine;
#if UNITY_EDITOR
using YourStudio.Sentinel.Editor;
#endif

public class MyComponent : MonoBehaviour
{
    [Tooltip("This reference is intentionally null at edit time")]
#if UNITY_EDITOR
    [SentinelIgnore("Assigned at runtime via Addressables")]
#endif
    public GameObject DynamicPrefab;
}
Tip: Wrap [SentinelIgnore] in #if UNITY_EDITOR to avoid including the attribute in player builds, since it lives in an editor-only assembly.

A complete working example is included at Assets/Sentinel/SentinelExtensionDemo/SentinelIgnoreDemo.cs.

Keyboard Shortcuts

ShortcutAction
Ctrl+Shift+Alt+SOpen Sentinel window
Menu PathAction
Window > Sentinel > Open WindowOpens the main Sentinel window
Window > Sentinel > Install Git Post-Merge HookInstalls a git hook that runs a scan after every pull / merge
Tools > Sentinel > CI > Run Scan (Headless)Runs a scan and writes reports (same as the CLI entry point)
Tools > Sentinel > CI > Lock Baseline (Headless)Locks the current issue state as baseline
Edit > Project Settings > SentinelOpens Sentinel settings
Assets > Sentinel > Exclude Folder from ScansRight-click context menu on folders
Assets > Sentinel > Suppress All Issues in This AssetRight-click context menu on assets

Files Written by Sentinel

FileLocationVersion Controlled?Purpose
SentinelDependencyCache.jsonLibrary/NoDependency graph cache (JSON)
SentinelSettings.jsonProjectSettings/YesShared scan configuration
suppressions.jsonAssets/SentinelData/YesShared issue suppressions
baseline.jsonAssets/SentinelData/YesShared issue baseline
SentinelTrends.assetUserSettings/NoPer-developer scan history
SentinelUserSettings.assetUserSettings/NoPer-developer UI preferences
SentinelBackups/*.zipLibrary/NoPre-deletion backup archives
SentinelTrash/Assets/OptionalReversible trash folder

FAQ

Does Sentinel affect my build size?

No. Sentinel is entirely editor-only. The assembly definition restricts it to the Editor platform. Nothing is included in player builds.

Does Sentinel modify my assets?

Only when you explicitly use a fix action (e.g., "Remove Missing Script" or "Clear Reference"). All fixes support Undo. The cleaner only deletes assets you manually select and confirm.

How does the closed scene scanner work?

Sentinel reads .unity files as raw YAML text and uses regex patterns to extract missing script GUIDs and broken references. It never calls EditorSceneManager.OpenScene(), so it's fast (50+ scenes per second) and doesn't disrupt your current scene.

Can I add custom detectors?

Yes. Create a class that extends SentinelExtensionBase, override the DetectorName property and whichever On*Scanned methods you need, and call AddIssue(). Sentinel discovers it automatically via TypeCache — no registration needed.

Why does the cleaner show an asset I know is used?

Make sure the scene or prefab using that asset is saved. The dependency tracer reads files on disk, not unsaved in-memory state. Also check that the using scene is in your Build Settings.

Can I undo a deletion?

If you used "Move to Trash," the assets are in Assets/SentinelTrash/ — move them back manually. If you used "Delete Permanently," a backup ZIP exists at Library/SentinelBackups/.

Support

Need Help?

Contact us at ragendom@gmail.com

Please include: