feat: compile-check detects build settings that moved since the last Unity build

Merged
HA
4d ago
+1275-44

Summary

  • uloop compile-check now refuses with COMPILE_CHECK_UNITY_BUILD_REQUIRED when a build setting moved since the last Unity build - a scripting define, a csc.rsp, an asmdef's defineConstraints / versionDefines, Packages/packages-lock.json or Packages/manifest.json - instead of replaying the old settings and missing errors.

User Impact

  • Before: these changes were invisible; the run reused what the last build recorded and could report a clean result for code that no longer compiles with the new settings.
  • After: the run stops with scripting define <NAME> is not in the last Unity build or <setting> changed since the last Unity build; uloop compile clears it.

Changes

  • Every run reads the active build target group from the response files' platform define and that group's scriptingDefineSymbols from ProjectSettings/ProjectSettings.asset. When the asset was saved after the last build (or there is no build-trace file), it refuses a define some response file lacks (added since the build). This runs before any record, so the first run sees it too. An asset older than the build is left to the record: its defines were in force when Unity built, so a missing one was left out on purpose - a Unity 6 build profile overriding Player Settings is the known case - and refusing it would never clear.
  • The first run after a Unity build records the build settings in Library/uloop/compile-check/<dag>/build-settings.json, keyed to the build-trace file Bee rewrites on every build (Library/Bee/tundra.log.json, TundraBuildState.state, fullprofile.json, newest wins). A later run with the same stamp refuses any recorded setting that moved; a new stamp means Unity built again, so the record is replaced, which clears the refusal.
  • csc.rsp is compared against the record only: Unity reads the asmdef folder's copy instead of Assets/csc.rsp and copies it unsplit, so reading defines back out of response files would refuse projects Unity builds.
  • A binary ProjectSettings, an unknown platform, or a Dedicated Server build (UNITY_SERVER) leaves the defines out of the comparison.
  • The record is written through the result store's atomic writer, now exported as resultstore.WriteFileAtomically.
  • The CLI VibeLog complete entry carries build_settings (stamp_found, record, refused_part, defines_checked), on refused runs too.
  • docs/compile-check.md, docs/vibe-logs.md, the skill and ADR 0018 (new item (h): build profile overrides) describe the comparison, its refusals and what stays unseen.

Verification

  • cli/dispatcher: gofmt -l . empty, go vet ./..., golangci-lint run ./... 0 issues, go test ./... -count=1 pass.
  • New tests cover the input-space table: refuse an added define; leave Assets/csc.rsp to the record; ignore the csc.rsp of an unbuilt asmdef; no stamp → no record; new / replaced / kept records; seven moved-setting cases; a refusal clears once the stamp moves; a record of another format version or not JSON is replaced; a define of a ProjectSettings older than the build is left to the record; the define scan stops at the next map; the report reaches BuildCompilePlan on a refusal and on success, and Run's result; binary ProjectSettings; group selection including Dedicated Server; separator splitting; an empty scriptingDefineSymbols map. VibeLog tests cover completed, refused, and early-failed runs.
  • Mutations (each reverted):
    • (a) defineTheBuildLacks always returns "" → TestDetectBuildSettingsChangeRefusesADefineTheLastBuildLacks fails.
    • (b) compare against any record regardless of stamp → ...ReplacesTheRecordAfterAnotherUnityBuild and ...ClearsARefusalOnceUnityBuildsAgain fail.
    • (c) firstDifference ignores the defines → the a removed define subtest fails.
    • (d) readBuildStamp always reports none → the new/replaced/kept/moved/clears/binary tests fail.
    • (e) loadBuildSettingsRecord ignores the format version → TestDetectBuildSettingsChangeReplacesARecordItCannotRead/another_format_version fails.
    • (f) always run the define check (drop the ProjectSettings mtime gate) → only ...LeavesADefineOfAnOlderProjectSettingsToTheRecord fails.
    • (g) drop the indent stop in scriptingDefinesValue → TestReadProjectDefinesStopsAtTheNextMap fails ("1" found for Android).
    • (h) drop withBuildSettingsReport on the refusal → TestBuildCompilePlanCarriesTheBuildSettingsReportOfARefusal fails; empty BuildPlan.BuildSettings → the plan and Run tests fail; empty Result.BuildSettings → TestRunReportsTheBuildSettingsComparison fails.
  • Live run on this repository's Unity project with the Editor closed, using a dist binary built from this branch:
    1. Editor open, uloop compile twice: the three build-trace files moved from 03:30:22 to 03:30:32 while the newest dll stayed at its earlier time. This is what the stamp relies on. They stayed put after quitting the Editor.
    2. compile-check: exit 0, VibeLog build_settings = {record: new, stamp_found: true}.
    3. ;CC_PROBE_ADDED appended to the Standalone defines: exit 1, scripting define CC_PROBE_ADDED is not in the last Unity build (measured before the stamp was moved ahead of the check; the log then showed stamp_found: false, which now reads true). Reverted.
    4. One existing Standalone define removed: exit 1, scripting defines of Standalone (removed: <DEFINE>) changed since the last Unity build, refused_part the same. Reverted → exit 0, record: kept.
    5. Assets/csc.rsp created with -nowarn:CS0168: exit 1, Assets/csc.rsp changed since the last Unity build. Removed → exit 0.
    6. git status --porcelain matched the state before the run.