Problem
The current resolver crashes and blocks CI if the Skipper API is unreachable. There is no cache fallback, and the sync mode can silently delete rows without opt-in. These are production-blocking safety gaps.
Proposed solution
Introduce three environment variables that make Skipper safe by default. All are opt-in escalations of danger, not opt-in safety.
SKIPPER_FAIL_OPEN (default: true)
If the API call fails and no valid cache exists, run all tests instead of crashing.
String cacheFile = System.getenv().getOrDefault("SKIPPER_CACHE_FILE", ".skipper-cache.json");
int cacheTtl = Integer.parseInt(System.getenv().getOrDefault("SKIPPER_CACHE_TTL", "300"));
boolean failOpen = !"false".equalsIgnoreCase(System.getenv().getOrDefault("SKIPPER_FAIL_OPEN", "true"));
try {
List<Row> rows = fetchFromSheets(config);
writeCacheFile(cacheFile, rows);
return rows;
} catch (Exception err) {
CacheResult cached = tryReadCache(cacheFile, cacheTtl);
if (cached != null) {
System.out.printf("[skipper] API failed, using cache (%ds old)%n", cached.ageSeconds);
return cached.rows;
}
if (failOpen) {
System.out.println("[skipper] API failed, no cache — running all tests (fail-open)");
return Collections.emptyList(); // empty = nothing disabled = all tests run
}
throw err;
}
SKIPPER_SYNC_ALLOW_DELETE (default: false)
In sync mode, never delete orphaned rows unless explicitly opted in.
boolean allowDeletes = "true".equalsIgnoreCase(System.getenv("SKIPPER_SYNC_ALLOW_DELETE"));
if (!allowDeletes && !rowsToDelete.isEmpty()) {
System.out.printf("[skipper] %d orphaned rows found.%n", rowsToDelete.size());
System.out.println("[skipper] Set SKIPPER_SYNC_ALLOW_DELETE=true to prune them.");
// Do not delete. Just warn.
}
Acceptance criteria
Effort estimate
~60 lines in the core resolver. No architecture changes required.
Problem
The current resolver crashes and blocks CI if the Skipper API is unreachable. There is no cache fallback, and the sync mode can silently delete rows without opt-in. These are production-blocking safety gaps.
Proposed solution
Introduce three environment variables that make Skipper safe by default. All are opt-in escalations of danger, not opt-in safety.
SKIPPER_FAIL_OPEN(default:true)If the API call fails and no valid cache exists, run all tests instead of crashing.
SKIPPER_SYNC_ALLOW_DELETE(default:false)In sync mode, never delete orphaned rows unless explicitly opted in.
Acceptance criteria
SKIPPER_FAIL_OPEN=true(default): API failure → run all tests, no crashSKIPPER_FAIL_OPEN=false: restores current crash behaviourSKIPPER_CACHE_FILE(default:.skipper-cache.json)SKIPPER_CACHE_TTLseconds (default: 300)SKIPPER_SYNC_ALLOW_DELETE=trueEffort estimate
~60 lines in the core resolver. No architecture changes required.