Skip to content

Commit 2e6028c

Browse files
wan9chiclaude
andcommitted
feat(cache): consume runner-aware tool reports for cache decisions
Step 6 of docs/runner-task-ipc/plan.md. - Apply `ignoreInputs` to filter inferred fspy reads (directory-aware) - Apply `ignoreOutputs` to filter auto-detected writes (overlap check + archive) - Short-circuit cache update on `disableCache()` via new `CacheNotUpdatedReason::ToolRequested` - Embed `tracked: true` envs in `PostRunFingerprint.tracked_envs`; validate on lookup by comparing against the current parent env - Recorder env_map sources from `std::env::vars_os()` so tools can resolve envs the user never declared - Bump cache schema to 13 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent cb99718 commit 2e6028c

8 files changed

Lines changed: 232 additions & 42 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vite_task/src/session/cache/display.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ pub fn format_cache_status_inline(cache_status: &CacheStatus) -> Option<Str> {
179179
let desc = format_input_change_str(*kind, path.as_str());
180180
return Some(vite_str::format!("○ cache miss: {desc}, executing"));
181181
}
182+
FingerprintMismatch::TrackedEnvChanged { name, .. } => {
183+
return Some(vite_str::format!(
184+
"○ cache miss: tracked env '{name}' changed, executing"
185+
));
186+
}
182187
};
183188
Some(vite_str::format!("○ cache miss: {reason}, executing"))
184189
}

crates/vite_task/src/session/cache/mod.rs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,12 @@ pub enum FingerprintMismatch {
158158
kind: InputChangeKind,
159159
path: RelativePathBuf,
160160
},
161+
/// A tool-tracked env var changed between runs.
162+
TrackedEnvChanged {
163+
name: Str,
164+
old: Option<Str>,
165+
new: Option<Str>,
166+
},
161167
}
162168

163169
impl Display for FingerprintMismatch {
@@ -175,6 +181,18 @@ impl Display for FingerprintMismatch {
175181
Self::InputChanged { kind, path } => {
176182
write!(f, "{}", display::format_input_change_str(*kind, path.as_str()))
177183
}
184+
Self::TrackedEnvChanged { name, old, new } => {
185+
write!(f, "tracked env {name}: ")?;
186+
match old {
187+
Some(value) => write!(f, "{:?}", value.as_str())?,
188+
None => write!(f, "(unset)")?,
189+
}
190+
write!(f, " → ")?;
191+
match new {
192+
Some(value) => write!(f, "{:?}", value.as_str()),
193+
None => write!(f, "(unset)"),
194+
}
195+
}
178196
}
179197
}
180198
}
@@ -215,16 +233,16 @@ impl ExecutionCache {
215233
"CREATE TABLE task_fingerprints (key BLOB PRIMARY KEY, value BLOB);",
216234
(),
217235
)?;
218-
conn.execute("PRAGMA user_version = 12", ())?;
236+
conn.execute("PRAGMA user_version = 13", ())?;
219237
}
220-
1..=11 => {
238+
1..=12 => {
221239
// old internal db version. reset
222240
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, true)?;
223241
conn.execute("VACUUM", ())?;
224242
conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, false)?;
225243
}
226-
12 => break, // current version
227-
13.. => {
244+
13 => break, // current version
245+
14.. => {
228246
return Err(anyhow::anyhow!("Unrecognized database version: {user_version}"));
229247
}
230248
}
@@ -262,11 +280,20 @@ impl ExecutionCache {
262280
return Ok(Err(CacheMiss::FingerprintMismatch(mismatch)));
263281
}
264282

265-
// Validate post-run fingerprint (inferred inputs from fspy)
266-
if let Some((kind, path)) = cache_value.post_run_fingerprint.validate(workspace_root)? {
267-
return Ok(Err(CacheMiss::FingerprintMismatch(
268-
FingerprintMismatch::InputChanged { kind, path },
269-
)));
283+
// Validate post-run fingerprint (inferred inputs + tracked envs)
284+
if let Some(mismatch) = cache_value.post_run_fingerprint.validate(workspace_root)? {
285+
let fingerprint_mismatch = match mismatch {
286+
crate::session::execute::fingerprint::PostRunMismatch::InputChanged {
287+
kind,
288+
path,
289+
} => FingerprintMismatch::InputChanged { kind, path },
290+
crate::session::execute::fingerprint::PostRunMismatch::TrackedEnvChanged {
291+
name,
292+
old,
293+
new,
294+
} => FingerprintMismatch::TrackedEnvChanged { name, old, new },
295+
};
296+
return Ok(Err(CacheMiss::FingerprintMismatch(fingerprint_mismatch)));
270297
}
271298
// Associate the execution key to the cache entry key if not already,
272299
// so that next time we can find it and report what changed

crates/vite_task/src/session/event.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ pub enum CacheNotUpdatedReason {
8484
/// inputs/outputs on the next hit. Carries the underlying error for
8585
/// user-facing reporting.
8686
IpcServerError(IpcServerError),
87+
/// A runner-aware tool explicitly requested that this run not be cached
88+
/// (e.g. vite dev-server, a watch task).
89+
ToolRequested,
8790
}
8891

8992
#[derive(Debug)]

crates/vite_task/src/session/execute/fingerprint.rs

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,20 @@ pub struct PostRunFingerprint {
2626
/// Paths inferred from fspy during execution with their content fingerprints.
2727
/// Only populated when `input_config.includes_auto` is true.
2828
pub inferred_inputs: HashMap<RelativePathBuf, PathFingerprint>,
29+
30+
/// Env vars observed via runner-aware IPC with `tracked: true`. Key is the
31+
/// env name; value is the env value at execution time (or `None` if unset).
32+
/// Validated at cache lookup by comparing against the current parent env.
33+
pub tracked_envs: BTreeMap<Str, Option<Str>>,
34+
}
35+
36+
/// A mismatch between the stored post-run fingerprint and the current state.
37+
#[derive(Debug, Clone)]
38+
pub enum PostRunMismatch {
39+
/// An inferred input file or directory changed.
40+
InputChanged { kind: InputChangeKind, path: RelativePathBuf },
41+
/// A tool-tracked env var changed value (or was added/removed).
42+
TrackedEnvChanged { name: Str, old: Option<Str>, new: Option<Str> },
2943
}
3044

3145
/// Fingerprint for a single path (file or directory)
@@ -64,11 +78,13 @@ impl PostRunFingerprint {
6478
/// * `inferred_path_reads` - Map of paths that were read during execution (from fspy)
6579
/// * `base_dir` - Workspace root for resolving relative paths
6680
/// * `globbed_inputs` - Prerun glob fingerprint; paths here are skipped
81+
/// * `tracked_envs` - Tool-requested env vars (name → value), validated on lookup
6782
#[tracing::instrument(level = "debug", skip_all, name = "create_post_run_fingerprint")]
6883
pub fn create(
6984
inferred_path_reads: &HashMap<RelativePathBuf, PathRead>,
7085
base_dir: &AbsolutePath,
7186
globbed_inputs: &BTreeMap<RelativePathBuf, u64>,
87+
tracked_envs: BTreeMap<Str, Option<Str>>,
7288
) -> anyhow::Result<Self> {
7389
let inferred_inputs = inferred_path_reads
7490
.par_iter()
@@ -80,16 +96,16 @@ impl PostRunFingerprint {
8096
})
8197
.collect::<anyhow::Result<HashMap<_, _>>>()?;
8298

83-
Ok(Self { inferred_inputs })
99+
Ok(Self { inferred_inputs, tracked_envs })
84100
}
85101

86-
/// Validates the fingerprint against current filesystem state.
87-
/// Returns `Some((kind, path))` if an input changed, `None` if all valid.
102+
/// Validates the fingerprint against current filesystem and env state.
103+
/// Returns `Some(mismatch)` on the first divergence, `None` if all valid.
88104
#[tracing::instrument(level = "debug", skip_all, name = "validate_post_run_fingerprint")]
89105
pub fn validate(
90106
&self,
91107
base_dir: &AbsolutePath,
92-
) -> anyhow::Result<Option<(InputChangeKind, RelativePathBuf)>> {
108+
) -> anyhow::Result<Option<PostRunMismatch>> {
93109
let input_mismatch = self.inferred_inputs.par_iter().find_map_any(
94110
|(input_relative_path, path_fingerprint)| {
95111
let input_full_path = Arc::<AbsolutePath>::from(base_dir.join(input_relative_path));
@@ -115,11 +131,27 @@ impl PostRunFingerprint {
115131
} else {
116132
input_relative_path.clone()
117133
};
118-
Some(Ok((kind, path)))
134+
Some(Ok(PostRunMismatch::InputChanged { kind, path }))
119135
}
120136
},
121137
);
122-
input_mismatch.transpose()
138+
if let Some(result) = input_mismatch {
139+
return result.map(Some);
140+
}
141+
142+
// Validate tracked envs against the current parent env.
143+
for (name, stored_value) in &self.tracked_envs {
144+
let current_value =
145+
std::env::var_os(name.as_str()).and_then(|v| v.to_str().map(Str::from));
146+
if current_value.as_ref() != stored_value.as_ref() {
147+
return Ok(Some(PostRunMismatch::TrackedEnvChanged {
148+
name: name.clone(),
149+
old: stored_value.clone(),
150+
new: current_value,
151+
}));
152+
}
153+
}
154+
Ok(None)
123155
}
124156
}
125157

0 commit comments

Comments
 (0)