Problem
Any flow that caches LLM or API results currently requires a 5-step pattern:
- buildCacheKey — deterministic key from inputs
- readCacheFile — file-read with
onError: continue
- checkCache — evaluate hit/miss/expired (age-based TTL)
- buildCacheEntry — quality gate + serialize (after computation)
- writeCacheFile — persist to disk (guarded on truthy output)
This pattern must be implemented identically in every cached flow. In a 90+ flow codebase, 12 flows use this pattern = 60 steps dedicated to caching.
Brittleness
The pattern has 4 known failure modes that teams build regression tests to catch:
- Step ordering bug —
buildCacheEntry references $.steps.buildResult but is declared before it. If execution order changes, cache writes stale data.
- Null guard missing —
writeCacheFile must check $.steps.buildCacheEntry.output is truthy. Without this guard, it writes null or undefined to the cache file.
- onError missing —
readCacheFile, buildCacheEntry, and writeCacheFile all need onError: continue. Missing any one causes the flow to fail on cache corruption.
- TTL inconsistency — each flow hardcodes its own TTL calculation. Easy to have different TTL logic across flows.
Proposal
Add a first-class cache step type:
{
"id": "cached",
"type": "cache",
"cache": {
"namespace": "company-research",
"key": "{{$.input.company}}-{{$.input.stage}}",
"ttl": "30d",
"qualityGate": {
"minLength": 100,
"requiredFields": ["data", "score"]
},
"steps": [
{ "id": "doExpensiveWork", "type": "bash", "bash": { "command": "..." } },
{ "id": "buildResult", "type": "code", "code": { "source": "..." } }
]
}
}
Semantics:
- On cache hit (key exists, not expired, passes quality gate): skip inner steps, return cached data
- On cache miss/expired: execute inner steps, validate output against quality gate, write to cache
- Cache key is deterministic from the template expression
- TTL supports human-readable durations:
3d, 30d, 1h
- Quality gate is optional; if present, prevents caching bad results
- All file I/O and error handling is managed by the engine
Benefits
- 12 flows × 5 steps = 60 steps reduced to 12 cache blocks
- Eliminates 4 known brittleness patterns
- Standardizes TTL format (no more
30 * 24 * 60 * 60 * 1000 in code)
- Quality gates become declarative, not imperative
- Cache invalidation becomes a platform feature (
one cache clear --namespace X)
Related
Problem
Any flow that caches LLM or API results currently requires a 5-step pattern:
onError: continueThis pattern must be implemented identically in every cached flow. In a 90+ flow codebase, 12 flows use this pattern = 60 steps dedicated to caching.
Brittleness
The pattern has 4 known failure modes that teams build regression tests to catch:
buildCacheEntryreferences$.steps.buildResultbut is declared before it. If execution order changes, cache writes stale data.writeCacheFilemust check$.steps.buildCacheEntry.outputis truthy. Without this guard, it writesnullorundefinedto the cache file.readCacheFile,buildCacheEntry, andwriteCacheFileall needonError: continue. Missing any one causes the flow to fail on cache corruption.Proposal
Add a first-class cache step type:
{ "id": "cached", "type": "cache", "cache": { "namespace": "company-research", "key": "{{$.input.company}}-{{$.input.stage}}", "ttl": "30d", "qualityGate": { "minLength": 100, "requiredFields": ["data", "score"] }, "steps": [ { "id": "doExpensiveWork", "type": "bash", "bash": { "command": "..." } }, { "id": "buildResult", "type": "code", "code": { "source": "..." } } ] } }Semantics:
3d,30d,1hBenefits
30 * 24 * 60 * 60 * 1000in code)one cache clear --namespace X)Related