Lesson 268: Unity CI Concurrency Cancel Library Poison Receipt on BUILD_RECEIPT (2026)

Direct answer: Before November 2026 fest Addressables promotion, promote ci_cancel_safe_receipt_v1.json with X1–X6 gates—split concurrency groups so lint may cancel but Addressables may not mid-catalog, add rm -rf Library cleanup with if: always() after cancel weeks, document X4 cancel repro, and fail-close addressables_build_exclusive_ok on BUILD_RECEIPT. Pair Concurrency cancel preflight (Guide #30), 3-day no false green challenge scenario F, and cousin Lesson 261 (unity_ci_library_cache_receipt_v1 cache key—not cancel boundaries).

Lesson hero for Unity CI concurrency cancel Library poison receipt

Why this matters now (November CI cancel edges)

November 2026 teams add concurrency: cancel-in-progress on main to stop double CI—then a fast lint job and a slow Addressables player build share one group. Cancel fires during AddressablesPlayerBuildProcessor; the next job restores a half-written Library/ and passes compile until fest players hit InvalidKeyException. Lesson 267 wires async playtest CSV honesty; 268 wires CI cancel boundaries before Wednesday smoke.

Library cache preflight owns ContentState hash in the cache key; 268 owns cancel policy and post-cancel cleanup on BUILD_RECEIPT.

Beginner path (split groups → cleanup → receipt)

Step Action Success check
1 Put lint and Addressables in different concurrency groups X1 group names logged
2 Set cancel-in-progress: false on Addressables job X2 pass
3 Add cleanup-after-addressables with rm -rf Library X3 if: always()
4 Reproduce two cancels + third green build X4 cancel repro log
5 Wire fail-closed jq before merge X5 addressables_build_exclusive_ok
6 File receipt + BUILD_RECEIPT column ci_cancel_safe_ok: true

Time: ~55 minutes first November cancel week; ~15 minutes when workflow template exists.

Developer path (gates X1–X6)

Gate Check Fail when
X1 Split concurrency groups in workflow YAML Lint and Addressables share one group
X2 Addressables job has no mid-build cancel cancel-in-progress: true on catalog job
X3 Post-cancel rm -rf Library step Cleanup skipped when job cancelled
X4 Cancel mid-build repro documented No log of forced cancel + recovery
X5 Fail-closed promotion jq Merge when addressables_build_exclusive_ok false
X6 ci_cancel_safe_receipt_v1.json ci_cancel_safe_ok false at promote

Align with Guide #30 preflight—guide = ninety-second gate; 268 = BUILD_RECEIPT milestone.

Cousin receipt crosswalk

Field Cousin (261) Cousin (3-day F) This lesson (268)
Schema unity_ci_library_cache_receipt_v1 addressables_ci_cache_receipt_v1 ci_cancel_safe_receipt_v1
Scope Cache key + ContentState hash Three-day cache ladder concurrency split + post-cancel cleanup
Path release-evidence/unity/ci-cache/ release-evidence/unity/ci-cache-ladder/ release-evidence/unity/ci-cancel/

Do not merge schemas—reference paths in cousin_receipts only.

X1 — Split workflow concurrency groups

# .github/workflows/unity-fast.yml
concurrency:
  group: unity-fast-${{ github.ref }}
  cancel-in-progress: true

# .github/workflows/unity-addressables-player.yml
concurrency:
  group: unity-addressables-exclusive-${{ github.ref }}
  cancel-in-progress: false

Rule: Never share one concurrency.group between fast jobs and Addressables player jobs.

X2 — Disable cancel on Addressables-exclusive job

Single workflow file alternative:

jobs:
  addressables-build:
    concurrency:
      group: unity-addressables-exclusive-${{ github.ref }}
      cancel-in-progress: false

Document job name in receipt—Thursday row review column addressables_build_exclusive_ok.

X3 — Post-cancel Library cleanup

  cleanup-after-addressables:
    needs: addressables-build
    if: always()
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: rm -rf Library Temp

Run even when addressables-build is cancelled—partial StreamingAssets/aa must not cache forward.

X4 — Cancel repro matrix

  1. Push commit A; start Addressables build.
  2. Push commit B while A runs (trigger cancel if misconfigured).
  3. Let job C start; verify Library/ was cleaned (X3).
  4. Player smoke on artifact from C—catalog loads without InvalidKeyException.

Log cancel count and job ids in receipt cancel_repro_log. Pair 3-day challenge scenario F.

X5 — Fail-closed promotion jq (CI tail)

jq -e '.addressables_build_exclusive_ok == true' release-evidence/unity/ci-cancel/CI_CANCEL_SAFE_RECEIPT.json

Block merge to fest branch when receipt missing after any cancel week—pair Wednesday smoke.

ci_cancel_safe_receipt_v1.json

{
  "schema": "ci_cancel_safe_receipt_v1",
  "build_label": "november-closeout-2026-rc2",
  "workflow_paths": {
    "fast": ".github/workflows/unity-fast.yml",
    "addressables_exclusive": ".github/workflows/unity-addressables-player.yml"
  },
  "concurrency_groups": {
    "fast": "unity-fast-${{ github.ref }}",
    "addressables_exclusive": "unity-addressables-exclusive-${{ github.ref }}"
  },
  "addressables_job": "addressables-build",
  "cancel_in_progress_on_addressables": false,
  "post_cancel_cleanup_job": "cleanup-after-addressables",
  "cancel_repro_log": {
    "forced_cancel_count": 2,
    "recovery_job_id": "987654321",
    "player_smoke_invalid_key": false
  },
  "cousin_receipts": {
    "library_cache": "release-evidence/unity/ci-cache/UNITY_CI_LIBRARY_CACHE_RECEIPT.json",
    "playtest_csv_ingest": "release-evidence/playtest/playtest_csv_ingest_receipt_v1.json"
  },
  "gates": {
    "X1_split_groups": "pass",
    "X2_no_cancel_on_addressables": "pass",
    "X3_post_cancel_cleanup": "pass",
    "X4_cancel_repro": "pass",
    "X5_fail_closed": "pass",
    "X6_receipt": "pass"
  },
  "addressables_build_exclusive_ok": true,
  "ci_cancel_safe_ok": true,
  "content_update_promotion_allowed": true
}

Pin under release-evidence/unity/ci-cancel/CI_CANCEL_SAFE_RECEIPT.json.

BUILD_RECEIPT columns (November lane)

Column Source Pass when
addressables_build_exclusive_ok X5 jq true
ci_cancel_safe_ok X6 receipt true
library_cache_bust_ok Lesson 261 cousin true when strip week
wednesday_smoke_allowed Lesson 267 cousin true

Key takeaways

  1. Split concurrency groups—lint may cancel; Addressables may not mid-catalog.
  2. rm -rf Library after cancel with if: always()—non-negotiable on fest months.
  3. Fail-closed addressables_build_exclusive_ok before branch promotion.
  4. Run Library cache preflight before cancel policy on strip weeks.
  5. 3-day challenge scenario F validates cancel + clean ladder.
  6. Invalid Key help triages runtime; 268 prevents false-green promotion.
  7. 15-free GitHub Actions CI recipes bookmark build graphs—cancel policy is separate column.
  8. Cousin Lesson 261 owns cache key material—not cancel boundaries.

Common mistakes

  • One concurrency: group: unity-main for lint + Addressables (X1 fail).
  • cancel-in-progress: true on Addressables job during catalog writes (X2 fail).
  • Skipping cleanup when job status is cancelled (X3 fail).
  • Blaming cache key when cancel poisoned Library/ (Lesson 261 cousin).
  • Promoting after cancel week without X4 repro on clean runner.
  • Merging ci_cancel_safe_receipt with unity_ci_library_cache_receipt schemas.

Troubleshooting

Symptom Lane
Random InvalidKey after busy CI day X1 split + X3 cleanup
Cancelled job, next job fast but player broken X3 if: always() missing
Only fails after second push same hour X2 disable cancel on Addressables
Cache key correct, still stale Library cache C3 + this X3
CI green after cancel, smoke fails Wednesday File 268 before smoke—not only Help #30 forward

Mini exercise (50 minutes)

  1. Audit workflow YAML—log X1 group names.
  2. Misconfigure shared group once—confirm X4 repro catches poison.
  3. Add X3 cleanup job with if: always().
  4. Wire X5 jq gate in workflow tail.
  5. File receipt; BUILD_RECEIPT GREEN for ci_cancel_safe_ok.
  6. Crosswalk cousin Lesson 261 path in cousin_receipts.

Continuity — November 2026 fest close-out + playtest triage (266–276)

Lesson Receipt focus
267 Discord CSV ingest
268 (this) Unity CI cancel poison
269 Godot VDF LF
270 GameMaker setlive dry-run (forward)

Previous: Lesson 267 — Discord playtest thread CSV ingest receipt
Next: Lesson 269 — Godot export VDF LF line ending steamcmd receipt

FAQ

Same as Lesson 261?
261 = actions/cache key + ContentState hash; 268 = concurrency split + post-cancel Library/ cleanup.

Same as 3-day challenge scenario F?
Challenge owns F1–F6 ladder calendar; 268 owns ci_cancel_safe_receipt_v1 BUILD_RECEIPT column.

Same as Guide #30?
Guide = ninety-second preflight; 268 = course milestone with promotion jq + cancel repro log.

Help #30 when it ships?
Fix lane when cancel already corrupted Library/; 268 prevents promotion without X1–X6.

Resource #28 when it ships?
Bookmark list for safe concurrency patterns—see 12 patterns listicle until Resource row publishes.

Can we delete actions/cache instead?
No—run Lesson 261 cache key lane and 268 cancel lane.

Does this replace Wednesday smoke?
No—binary smoke is separate; 268 stops false-green CI before smoke runs.


November CI lies when cancel-in-progress interrupts Addressables—split groups, clean Library/ after cancel, fail-closed addressables_build_exclusive_ok, then promote.