Bounded typed execution cache for LUMI agent hot paths
| Field | Value |
|---|---|
| Version | GA (modern-only API) |
| Implementation | src/core/joyride/ |
| Public entrypoint | @core/joyride |
| Contract | JoyRideContract.ts |
| Test coverage | 179+ unit tests |
- Abstract
- Problem statement
- System overview
- Typed decision model
- Cache architecture
- Cache key taxonomy
- Invalidation model
- Command classifier
- Verification proof model
- Search invalidation
- Scratch artifact lifecycle
- Memory budget and eviction
- Security model
- Operational modes
- Observability
- Public API contract
- Integration boundaries
- Performance characteristics
- Failure modes
- Testing and contract enforcement
- Non-goals and future work
- References
- Appendix A — Reason code catalog
- Appendix B — Safe-readonly allowlist
- Appendix C — Decision state machine
- Appendix D — Trust model comparison
- Appendix E — Document revision history
JoyRide is LUMI's in-process execution cache for agent coding hot paths. It reduces latency in repeated command, search, and verification loops by reusing only validated, bounded, invalidation-aware cache entries.
Unlike general-purpose agent "memory" systems, JoyRide is explicitly cache infrastructure: session-scoped, TTL-bound, evictable, and fail-closed. Every lookup returns a typed JoyRideCacheDecision with stable reason codes and explicit fallback behavior. Integrations never receive ambiguous boolean results.
JoyRide is production-hardened with secret scanning, degraded-mode fallback, contract-tested export surfaces, and comprehensive real-session validation — without UI.
During active agent sessions, LUMI repeatedly executes:
inspect → modify → command → verify → search → inspect → ...
Each phase may re-run identical safe operations (git status, grep for a symbol, npm test after unrelated edits). Without caching, latency accumulates. With naive caching, correctness fails silently.
| Failure | Consequence |
|---|---|
| Stale verification reuse | Agent believes tests pass when they would fail |
| Secret retention | Credentials in memory; leak via diagnostics |
| Unbounded growth | RAM pressure; editor slowdown |
| Unknown command reuse | Arbitrary shell execution skipped |
| Cross-task leakage | Task A output reused in Task B |
| Ambiguous hit/miss API | Call sites invent unsafe policy |
Improve agent ergonomics (responsiveness, iteration speed) without increasing stale-state risk, hidden retention, or operational ambiguity.
┌──────────────────────────────────────────────────────────────────┐
│ LUMI runtime integrations │
│ task/index.ts · SearchFilesToolHandler · AttemptCompletionHandler│
│ extension.ts │
└────────────────────────────┬─────────────────────────────────────┘
│ @core/joyride (frozen exports)
┌────────────────────────────▼─────────────────────────────────────┐
│ JoyRideHotPath │
│ lookupSafeCommandResult · lookupSearchResult · lookupVerification │
│ storeReusableCommandResult · storeSearchResult · storeVerification│
│ storeScratchArtifactWithCleanup │
└────────────┬───────────────────────────────┬─────────────────────┘
│ │
┌────────────▼────────────┐ ┌─────────────▼─────────────────────┐
│ JoyRideCommandClassifier │ │ JoyRideContext │
│ allowlist · unsafe detect│ │ workspace snapshot · task scope │
└────────────┬────────────┘ └─────────────┬─────────────────────┘
│ │
┌────────────▼───────────────────────────────▼─────────────────────┐
│ JoyRideCache │
│ Map storage · validation · eviction · secret scan · cleanup │
└────────────────────────────┬───────────────────────────────────────┘
│
┌────────────────────────────▼───────────────────────────────────────┐
│ Cross-cutting: JoyRideDecisions · JoyRideReasonCodes · │
│ JoyRideDecisionLog · JoyRideAudit · JoyRideDiagnostics · │
│ JoyRideConfig · JoyRideLifecycle │
└────────────────────────────────────────────────────────────────────┘
- Integration calls
lookupSafeCommandResult(cache, command, scope, ...) - Config gate: disabled →
disableddecision; degraded → no trusted hits - Classifier: command tier determines reuse eligibility
- Workspace snapshot built (fingerprints for cwd, git, lockfiles, deps, env)
- Cache key materialized from stable JSON + SHA-256
cache.get(key, validationFingerprint)— validation compares stored vs current proof- Hit →
hitDecisionwith value + audit; miss/stale → appropriate decision - Decision recorded in bounded decision log
- Integration calls
storeReusableCommandResultafter execution - Config gate: disabled → no-op
- Classifier:
canStoreDiagnosticcheck - Output summarized (bounded text, not raw multi-MB buffers)
- Secret scan on admission
cache.trySet(key, value, metadata)— budget check, generation guard, late-write rejection- Pressure trim if over budget
type |
canReuse |
Meaning |
|---|---|---|
hit |
true |
Safe reuse; value present |
miss |
false |
No reusable entry; execute normally |
stale |
false |
Entry exists but invalid; rerun |
rejected |
false |
Admission or policy refusal |
disabled |
false |
JoyRide disabled |
diagnosticOnly |
false |
Stored for stats; never skip |
degraded |
false |
Internal failure; no trusted reuse |
Every decision includes (enforced by JoyRideDecisionInvariants.test.ts):
interface JoyRideDecisionContext {
type: JoyRideDecisionType
canReuse: boolean
reasonCode: JoyRideReasonCode // stable vocabulary
reasonMessage: string // human-readable
fallbackBehavior: JoyRideFallbackBehavior
diagnosticOnly: boolean
degraded: boolean
auditEventId: string
// optional: keySummary, proofSummary, configExplanation, entryAgeMs, ...
}| Behavior | Caller action |
|---|---|
reuseCachedValue |
Return decision.value |
executeNormally |
Run work; no skip |
executeAndStoreDiagnosticOnly |
Run; store diagnostic if safe |
executeAndStoreReusableIfSafe |
Run; store if classifier allows |
rejectArtifact |
Do not admit artifact |
markStaleAndExecute |
Rerun; stale entry retained for diagnostics |
doNotStore |
JoyRide disabled |
disableActiveReuse |
Degraded — no skips |
function isJoyRideHitDecision(decision): decision is JoyRideHitDecision {
return decision.type === "hit" && decision.canReuse
}Integrations must use this guard — never check canReuse alone without type === "hit".
| Kind | Purpose | Default TTL | Default budget |
|---|---|---|---|
hotExecution |
Safe-readonly command summaries | 5 min | 4 MiB |
taskLocal |
Task-scoped metadata | varies | 8 MiB |
workspaceIndex |
Search/grep results | 3 min | 12 MiB |
verification |
Proven test/lint output | 10 min | 8 MiB |
scratchArtifact |
Temporary task artifacts | spec-defined | 8 MiB |
Each cache entry carries:
interface JoyRideCacheEntry<T> {
key: string
value: T
cacheKind: JoyRideCacheKind
scope: JoyRideCacheScope
ownerTaskId: string
ttl: number
estimatedBytes: number
fingerprint: string
workspaceFingerprint: string
approvalBoundaryId: string
generation: number
safetyClassification: JoyRideSafetyClassification
invalidationReason: JoyRideInvalidationReason[]
cleanupHandler: JoyRideCleanupHandler
// proof dimensions for validation
relevantFileHashes?: Record<string, string>
lockfileFingerprint?: string
dependencyFingerprint?: string
gitHead?: string
environmentFingerprint?: string
runtimeVersion?: string
toolVersion?: string
}Default: memoryOnly. JoyRide does not persist cache entries across VS Code sessions unless explicitly configured for diagnostic receipts (not used for active reuse).
Each task registers a generation counter. Writes from obsolete generations are rejected (reject.lateWrite). Bumping generation on cancellation prevents stale async writes from polluting cache state.
Keys are content-addressable: joyride:{namespace}:{sha256}.
| Namespace | Function | Input dimensions |
|---|---|---|
command-result |
createCommandResultCacheKey |
command, cwd, env/deps/git/runtime fingerprints |
grep-result |
createGrepResultCacheKey |
query, cwd, globs, workspace fingerprint, changed-file generation, case sensitivity, search impl version |
verification |
createVerificationCacheKey |
command, cwd, file hashes, all workspace fingerprints, approval boundary, runtime/tool versions |
scratch-artifact |
createScratchArtifactCacheKey |
taskId, artifact kind, content hash, generation, cleanup policy |
file-metadata |
createFileMetadataCacheKey |
path, file hash, mtime generation, workspace fingerprint |
diff |
createDiffCacheKey |
base/target hash, file path, taskId |
stableStringify() sorts object keys, handles circular references, normalizes undefined → "__undefined__". Fingerprints use SHA-256 over stable JSON — analogous to Turborepo/Nx task hash inputs.
Any change to key input dimensions produces a different key. Partial dimension overlap does not imply reuse eligibility — validation fingerprint must also match on get().
type JoyRideInvalidationReason =
| "ttl_expired"
| "task_completed" | "task_cancelled" | "task_boundary_changed"
| "workspace_drift" | "workspace_generation_changed"
| "file_hash_changed" | "git_head_changed"
| "lockfile_fingerprint_changed" | "dependency_fingerprint_changed"
| "approval_boundary_changed"
| "runtime_version_changed" | "tool_version_changed"
| "validation_failed" | "memory_pressure" | "emergency_pressure"
| "manual_flush" | "entry_replaced"
// ...cache.get(key, validationFingerprint) compares stored entry proof against current validation:
| Dimension mismatch | Result |
|---|---|
| File hash changed | Stale — stale.fileHashChanged |
| Git HEAD changed | Stale — stale.gitHeadChanged |
| Lockfile changed | Stale — stale.lockfileChanged |
| Workspace generation changed | Stale — stale.workspaceGenerationChanged |
| Approval boundary changed | Stale — stale.approvalBoundaryChanged |
| Task generation changed | Stale — stale.taskGenerationChanged |
| Event | Action |
|---|---|
| Task completion | flushTaskGeneration(taskId) |
| Task cancellation | bumpTaskGeneration + flush |
| Workspace drift | flushWorkspace(fingerprint) |
| Extension deactivate | shutdownJoyRideCache() |
buildJoyRideWorkspaceSnapshot(cwd, terminalMode, changedFileGeneration):
| Field | Source |
|---|---|
workspaceFingerprint |
hash(cwd + gitHead + changedFileGeneration) |
gitHead |
latest commit or no-git |
dependencyFingerprint |
stat hashes of package.json, pyproject.toml, Cargo.toml, go.mod, Gemfile, tsconfig.json |
lockfileFingerprint |
stat hashes of package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lockb, Cargo.lock, go.sum, poetry.lock |
environmentFingerprint |
hash(cwd + terminalMode + runtime + platform + arch) |
changedFileGeneration |
monotonic counter from task layer |
| Tier | Examples | Skip? | Store? |
|---|---|---|---|
safe-readonly |
pwd, git status, git rev-parse HEAD |
Yes | Yes |
verification |
npm test, eslint, tsc |
Only with proof | Yes (diagnostic default) |
diagnostic-store-only |
unknown commands, partial matches | Never | Sometimes |
no-store |
empty command | Never | Never |
Before allowlist matching, commands are analyzed for:
- Shell chaining:
;,|,&&,|| - Redirections:
<,>,>> - Subshells:
$(), backticks - Variable substitution:
$VAR,${VAR} - Env assignment:
export,KEY=valueprefix - Relative/absolute binary paths:
./git,/usr/bin/... - Quoted operator bypass attempts
- Env-altering:
npm install,git commit,git config - Network/mutation:
curl,rm,sudo
Analysis uses stripQuotesForShellAnalysis() to prevent quoted-operator bypass.
Active reuse requires positive proof of safety via explicit prefix/exact patterns — not absence of blocklist matches. New safe commands require deliberate allowlist addition + contract tests.
validateVerificationProof() requires all of:
| Field | Purpose |
|---|---|
relevantFileHashes |
Non-empty map of source file → hash |
workspaceFingerprint |
Current workspace identity |
approvalBoundaryId |
User approval scope |
gitHead |
VCS state |
dependencyFingerprint |
Manifest state |
lockfileFingerprint |
Lockfile state |
environmentFingerprint |
Runtime environment |
runtimeVersion |
Node/runtime version |
toolVersion |
Verification tool identity (lumi-verification-v1) |
Missing any field → miss.verification.incompleteProof or miss.verification.missingFileHashes.
A verification hit requires:
- Complete proof at lookup time
- Matching cache key (all dimensions)
- Validation fingerprint match on
get() - Entry not
diagnosticOnly(failed tests stored diagnostic-only) - JoyRide not disabled/degraded
- Verification cache not disabled via config
Commands with non-zero exit codes or failure markers in output are stored diagnosticOnly: true. They are never reused as proof of passing tests.
Search reuse requires identity across:
query(exact)cwdincludeGlobs/excludeGlobscaseSensitive(default: true)workspaceFingerprintchangedFileGenerationsearchImplementationVersion(currently versioned constant in hot path)
Any dimension change → miss with specific reason code (miss.search.queryChanged, miss.search.includeGlobChanged, etc.).
JoyRide does not implement "similar query" or semantic search caching. Keys are exact — analogous to grep cache keys in ripgrep-aware build tools.
| Requirement | Rejection code |
|---|---|
| Scratch cache enabled | reject.scratchCacheDisabled |
ownerTaskId present |
reject.missingOwnerTask |
Positive ttlMs |
reject.missingTTL |
cleanupHandler defined |
reject.missingCleanupHandler |
Positive estimatedBytes |
reject.oversized |
- Task flush (
flushTaskGeneration) - Task cancellation (generation bump + flush)
- Shutdown (
shutdownJoyRide) - TTL expiry
- Pressure trim / emergency trim
Cleanup is idempotent — cleanupInvokedKeys prevents double invocation. Failures increment cleanupFailureCount without crashing runtime.
const DEFAULT_BUDGET = {
maxTotalBytes: 32 * MiB,
maxEntryBytes: 512 * KiB,
maxPerTaskBytes: 8 * MiB,
maxArtifactCount: 128,
maxArtifactBytes: 1024 * KiB,
perKindBudgetBytes: {
hotExecution: 4 * MiB,
taskLocal: 8 * MiB,
workspaceIndex: 12 * MiB,
verification: 8 * MiB,
scratchArtifact: 8 * MiB,
},
emergencyTargetRatio: 0.35,
}- TTL-expired entries
- LRU within kind when per-kind budget exceeded
- Pressure trim when total budget exceeded
- Emergency trim to 35% of total budget under severe pressure
Conservative 1.25× overhead multiplier on all estimates. Oversized entries rejected at admission.
Admission scans for:
- Anthropic/OpenAI/GitHub/Slack/AWS key formats
- Bearer tokens
api_key=,secret=,npm_token=assignments- PEM private keys
.envvalue patterns
Key-name heuristic: apiKey, secret, token, authorization, password, privateKey, etc.
- Entry rejected:
reject.secretDetected - Stats:
rejectedUnsafeEntryCount++ - Diagnostics: count only — never raw secret content
- Bug-report snapshot: rejection counts in summary
public | workspace | taskLocal | sensitive | unsafe — assigned at admission; influences trim priority and diagnostic retention.
| Mode | canJoyRideStore() |
canJoyRideSkipWork() |
Use case |
|---|---|---|---|
enabled |
✓ | ✓ (unless degraded) | Production default |
diagnostics-only |
✓ | ✗ | Observe cache behavior without skipping |
disabled |
✗ | ✗ | Instant kill switch |
| degraded (runtime) | best-effort | ✗ | Internal failure recovery |
Independent disable for command reuse, verification cache, search cache, scratch cache — all forced off when mode=disabled.
| Variable | Aliases |
|---|---|
JOYRIDE_MODE |
on/enabled, diagnostics/diagnostics-only/observe, off/disabled |
JOYRIDE_COMMAND_REUSE |
JOYRIDE_COMMAND_REUSE_DISABLED |
JOYRIDE_VERIFICATION_CACHE |
JOYRIDE_VERIFICATION_CACHE_DISABLED |
JOYRIDE_SEARCH_CACHE |
JOYRIDE_SEARCH_CACHE_DISABLED |
JOYRIDE_SCRATCH_CACHE |
JOYRIDE_SCRATCH_CACHE_DISABLED |
Config loads at module init; safe to reload via loadJoyRideConfigFromEnv().
- Ring buffer: 128 entries max
- Every lookup/store decision recorded
- Query:
getJoyRideDecisionLog(limit),getLastJoyRideDecision() - Explain:
explainJoyRideDecision(decision)
Active skips (actual reuse events) recorded separately:
interface JoyRideCacheHitAudit {
key: string
cacheKind: JoyRideCacheKind
hitSource: "command" | "verification" | "grep"
ownerTaskId: string
reuseReason: string
entryAgeMs: number
validationFingerprintSummary: string
}buildJoyRideDiagnosticReport(cache) includes:
- Config + explanation
- Degraded state + reason
- Full stats (
JoyRideCacheStats) - Decision log size
- Recent audit trail (16 entries)
- Summary: hits, rejections, trim events, cleanup failures
createJoyRideBugReportSnapshot(cache) — JSON for issue attachments. Bounded, no secret content.
summarizeJoyRideHealth(cache) — one-line operational status:
helping=true hits=12 entries=8 degraded=false decisions=16
Exact export surface defined in JOYRIDE_FROZEN_EXPORTS (69 symbols). Contract drift test fails on any addition/removal without review.
Legacy APIs permanently removed:
lookupCommandResult,storeCommandResultlookupGrepResult,storeGrepResultJoyRideIntegration- Internal:
JoyRideCacheclass, key builders, decision constructors,resetJoyRideForTest
| Lookup | Store |
|---|---|
lookupSafeCommandResult |
storeReusableCommandResult |
lookupSearchResult |
storeSearchResult |
lookupVerificationProof |
storeVerificationProof |
| — | storeScratchArtifactWithCleanup |
| — | storeCommandDiagnostic |
| — | storeFailedVerificationDiagnostic |
Only @core/joyride. Forbidden direct imports:
/JoyRideCache,/JoyRideHotPath,/joyride/keys/JoyRideDecisionLog,/JoyRideDecisions,/JoyRideAudit
Integrations must not call:
getJoyRideCache().get(getJoyRideCache().set(getJoyRideCache().trySet(getJoyRideCache().flushTask(getJoyRideCache().invalidate
src/core/task/index.ts
src/core/task/tools/handlers/SearchFilesToolHandler.ts
src/core/task/tools/handlers/AttemptCompletionHandler.ts
src/extension.ts
Enforced by JoyRideImportBoundary.test.ts.
JoyRideBenchmark.test.ts enforces generous CI thresholds:
| Operation | Threshold |
|---|---|
| 200 inserts | < 500 ms |
| 200 lookups | < 50 ms |
| Stats snapshot | < 20 ms |
| Bug-report snapshot | < 100 ms |
| Task flush | < 500 ms |
| Typed decision (command + search) | < 200 ms |
- Low allocation on lookup path
- Bounded decision log append (O(1))
- Flush chunked (64 entries per call) to avoid latency spikes
- No unbounded maps/arrays/logs
- Command output summarized (12 KiB default max in summaries)
JoyRide target: invisible overhead — cheap enough to leave enabled in production.
| Failure | Behavior |
|---|---|
| Internal lookup error | degraded.internalFailure; execute normally |
| Size estimator failure | Degraded mode; no trusted reuse |
| Diagnostic logging failure | Swallowed; agent continues |
| Cleanup handler throws | cleanupFailureCount++; no crash |
| Cache manager error | Degraded; bug snapshot includes state |
| Late write after cancel | reject.lateWrite; entry not stored |
| Secret detected | reject.secretDetected; not stored |
Invariant: JoyRide must never prevent agent execution.
| Category | Suites |
|---|---|
| Contract drift | JoyRideContractDrift, JoyRideModernApi |
| Import boundaries | JoyRideImportBoundary |
| Decision invariants | JoyRideDecisionInvariants |
| Config modes | JoyRideConfigContract, JoyRideDegradedContract |
| Reason codes | JoyRideReasonCodes |
| Real sessions | JoyRideRealSession, JoyRideDogfood |
| GA strictness | JoyRideVerificationGa, JoyRideSearchGa, JoyRideScratchGa |
| Failure modes | JoyRideFailureModes |
| Performance | JoyRideBenchmark |
| Core cache | JoyRideCache, JoyRideHardening, JoyRideProduction |
npm run test:unit -- --grep "JoyRide"Full unit suite runs in .github/workflows/test.yml — JoyRide tests included, not isolated.
- UI, dashboards, status bar controls
- Cross-session persistence for active reuse
- Agent memory / narrative continuity
- Semantic or fuzzy search caching
- Distributed / remote cache tier (local-only today)
- Compatibility wrappers for legacy APIs
- File metadata cache hot-path wiring
- Diff cache integration
- Spill-to-disk for diagnostic receipts (not active reuse)
- Additional allowlist entries with contract tests
Any future work must preserve: typed decisions, fail-closed behavior, import boundaries, and contract test coverage.
| Document | Path |
|---|---|
| Documentation hub | src/core/joyride/docs/README.md |
| Package README | src/core/joyride/README.md |
| Brief | src/core/joyride/docs/BRIEF.md |
| Philosophy | src/core/joyride/docs/PHILOSOPHY.md |
| Caching model | src/core/joyride/docs/CACHING.md |
| API reference | src/core/joyride/docs/API.md |
| Glossary | src/core/joyride/docs/GLOSSARY.md |
| Troubleshooting | src/core/joyride/docs/TROUBLESHOOTING.md |
| Operator guide | docs/features/joyride.mdx |
| Release notes | docs/features/joyride-release-notes.mdx |
| System | Relevant concept |
|---|---|
| Bazel action cache | Content-addressable keys; hermetic inputs |
| Turborepo caching | Task hash inputs; cache hit/miss semantics |
| Nx computation caching | Input-based invalidation |
| HTTP caching (RFC 9111) | Validator-based freshness |
| Redis eviction policies | Bounded memory + eviction |
| OpenTelemetry | Structured observability without UI |
JoyRide adapts these patterns for session-scoped agent execution — it is not a port of any single system.
Complete stable vocabulary from JoyRideReasonCodes.ts. Vague standalone codes are forbidden by contract (JOYRIDE_FORBIDDEN_VAGUE_REASONS).
| Code | Meaning |
|---|---|
hit.command.safeAllowlisted |
Safe readonly command reused |
hit.search.workspaceFingerprintMatched |
Search key + validation matched |
hit.verification.completeProofMatched |
Verification proof complete |
| Code | Meaning |
|---|---|
miss.config.disabled |
JoyRide disabled |
miss.config.diagnosticsOnly |
Diagnostics-only — no skip |
miss.config.commandReuseDisabled |
Command reuse flag off |
miss.config.verificationCacheDisabled |
Verification cache off |
miss.config.searchCacheDisabled |
Search cache off |
miss.cacheDegraded |
Degraded — reuse suspended |
| Code | Meaning |
|---|---|
miss.noEntry |
No cached entry |
miss.expired |
Entry TTL expired |
miss.command.unknown |
Unknown command — never skip |
miss.command.unsafeSyntax |
Unsafe shell syntax |
miss.command.notAllowlisted |
Not on safe-readonly allowlist |
miss.command.envAltering |
Env-altering command |
miss.command.diagnosticOnly |
Cached entry is diagnostic-only |
| Code | Meaning |
|---|---|
miss.verification.missingFileHashes |
No file hash proof |
miss.verification.incompleteProof |
Proof fingerprint incomplete |
| Code | Meaning |
|---|---|
miss.search.noEntry |
No search entry |
miss.search.cwdChanged |
cwd dimension changed |
miss.search.queryChanged |
query changed |
miss.search.includeGlobChanged |
include glob changed |
| Code | Meaning |
|---|---|
stale.fileHashChanged |
Source hash mismatch |
stale.gitHeadChanged |
Git HEAD changed |
stale.lockfileChanged |
Lockfile fingerprint changed |
stale.workspaceGenerationChanged |
Workspace generation changed |
stale.taskGenerationChanged |
Task generation changed |
stale.approvalBoundaryChanged |
Approval boundary changed |
stale.validationFailed |
Validation fingerprint failed |
stale.marked |
Manually marked stale |
| Code | Meaning |
|---|---|
reject.secretDetected |
Secret pattern in value |
reject.oversized |
Entry exceeds size budget |
reject.scratchCacheDisabled |
Scratch cache off |
reject.missingCleanupHandler |
No cleanup handler |
reject.missingOwnerTask |
No owner task |
reject.missingTTL |
Invalid TTL |
reject.lateWrite |
Obsolete task generation |
reject.unscopedEntry |
Unscoped admission |
reject.cacheInternalError |
Internal admission error |
| Prefix | Examples |
|---|---|
degraded. |
degraded.internalFailure |
trim. |
trim.ttl, trim.lru, trim.pressure, trim.emergency |
cleanup. |
cleanup.success, cleanup.failure |
lifecycle. |
lifecycle.taskFlush, lifecycle.workspaceFlush, lifecycle.shutdown |
fallback. |
Internal markers — not decision types |
Explicit patterns from JoyRideCommandClassifier.ts. All require passing unsafe-syntax scan and not being env-altering.
pwd, whoami, hostname
| Pattern | Examples |
|---|---|
git status |
git status, git status -s |
git branch |
git branch, git branch -a |
git rev-parse HEAD |
git rev-parse HEAD |
git rev-parse --is-inside-work-tree |
|
git rev-parse --show-toplevel |
|
git diff --stat |
|
git ls-files |
|
git log --oneline |
|
ls, ls -la |
|
which <bin> |
which node |
node --version, npm --version |
|
python --version, python3 --version |
|
go version, rustc --version |
Not allowlisted: any command with ;, |, &&, redirects, subshells, ./binary, mutation verbs, network tools, or verification commands (those use proof gate instead).
stateDiagram-v2
[*] --> Lookup
Lookup --> Disabled: mode=disabled
Lookup --> Degraded: internal error
Lookup --> Classify: mode ok
Classify --> DiagnosticOnly: unknown/unsafe
Classify --> VerifyPath: verification cmd
Classify --> SafePath: safe-readonly
VerifyPath --> MissProof: incomplete proof
VerifyPath --> CacheGet: proof ok
SafePath --> CacheGet
CacheGet --> Hit: valid entry
CacheGet --> Stale: validation fail
CacheGet --> Miss: no entry
Hit --> [*]: canReuse=true
Stale --> [*]: markStaleAndExecute
Miss --> [*]: executeNormally
MissProof --> [*]: executeNormally
DiagnosticOnly --> [*]: execute, no skip
Disabled --> [*]: doNotStore
Degraded --> [*]: executeNormally
Only Hit with isJoyRideHitDecision() may skip work.
| Dimension | Naive cache | JoyRide |
|---|---|---|
| Lookup return type | boolean / T | undefined |
JoyRideCacheDecision |
| Unknown command | may hit | never skip |
| Verification | last output | 10-dimension proof |
| Search | query only | 8+ key dimensions |
| Secrets | may store | reject at admission |
| Memory bound | none | 32 MiB + trim |
| Disable | code deploy | env var |
| Failure | may throw | degraded + continue |
| Audit | optional logs | decision log + hit audit |
| API stability | informal | frozen + drift tests |
| Scratch | hope GC | cleanup handler required |
| Version | Date | Changes |
|---|---|---|
| 1.0 GA | 2026-06 | Modern-only API, frozen exports, 179+ contract tests |
| 1.1 docs | 2026-06 | CACHING, API, GLOSSARY, TROUBLESHOOTING, CONTRIBUTING, MIT license |
Documentation: src/core/joyride/docs/ · Implementation: src/core/joyride/*.ts · Contract: JoyRideContract.ts
JoyRide source and documentation in src/core/joyride/ are MIT licensed — Copyright (c) CardSorting.
JoyRide: fast when safe. Silent when irrelevant. Explicit when questioned. Disabled when needed. Degraded when suspicious. Fail-closed always.