Skip to content
Prev Previous commit
Next Next commit
Add draft ADR-39266 for branch-aware cache-miss semantics
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
  • Loading branch information
github-actions[bot] and claude authored Jun 14, 2026
commit 89027a84b62835f2d0210fcd38e3378cd07820df
39 changes: 39 additions & 0 deletions docs/adr/39266-encode-branch-aware-cache-miss-semantics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# ADR-39266: Encode Branch-Aware Cache-Miss Semantics for Daily AIC Fallback

**Date**: 2026-06-14
**Status**: Draft

## Context

The daily AI-credits (AIC) guardrail relies on a usage cache restored via `actions/cache/restore` in the activation job. GitHub Actions caches are branch-scoped: a cache written by the conclusion job on one PR branch is invisible to the activation job running on a different branch. To cover this gap, an artifact-based fallback (`restore_aic_usage_cache_fallback.cjs`) downloads the most recent usage artifact when the cache is missing. Previously, "miss" was inferred from `cache-hit != 'true'`, but the primary cache key embeds `${{ github.run_id }}`, so an exact hit never recurs across runs — meaning `cache-hit` alone is unreliable and produced both false misses (needless fallback downloads) and false hits.

## Decision

We will detect daily AIC cache misses **semantically** from the restore step's actual outputs rather than from exact-hit status alone. The compiler (`compiler_activation_job_builder.go`) now emits a dedicated `detect-daily-aic-cache-miss` step that classifies a miss using both `cache-hit` and `cache-matched-key`: a restore that matched any key (including a restore-key prefix match) is treated as a hit, and only the absence of a matched key is treated as a true miss. The artifact fallback step then runs **only** when this detection reports `cache_miss == 'true'`. Step outputs are passed into shell logic via environment variables to remain template-injection-safe.

## Alternatives Considered

### Alternative 1: Keep `cache-hit != 'true'` as the miss signal
Continue gating the fallback on the existing exact-hit boolean. Rejected because the run-id-scoped primary key guarantees `cache-hit` is effectively always `false`, so this signal cannot distinguish a genuine miss from a restore-key prefix hit — yielding false positives and false negatives.

### Alternative 2: Always run the artifact fallback unconditionally
Drop miss detection and let the fallback script (a no-op when the cache file already exists) run every time. Rejected because it incurs unnecessary artifact lookups/downloads on every activation even when the restore already populated the file, adding latency and API load without changing the outcome.

## Consequences

### Positive
- Miss detection reflects the real restore result, eliminating both unnecessary fallback downloads and missed fallback opportunities.
- Behavior is correct under branch-scoped cache visibility, including cross-PR-branch activation.
- Step-output interpolation is passed through env vars, preserving the template-injection-safe pattern.

### Negative
- Adds an extra generated workflow step, increasing the activation job's surface area and maintenance cost.
- The shell detection logic is embedded as Go string literals in the compiler, which is harder to read and modify than a standalone script.

### Neutral
- Correctness now depends on `actions/cache/restore`'s `cache-matched-key` semantics for restore-key prefix matches.
- Golden workflow fixtures across all engines were regenerated to include the new activation-step sequence.

---

*This is a DRAFT ADR generated by the [Design Decision Gate](https://github.com/github/gh-aw/actions/runs/27507998081) workflow. The PR author must review, complete, and finalize this document before the PR can merge.*
Loading