Skip to content

Commit 29d81f1

Browse files
gmoonclaude
andcommitted
health/freshness: index-aware for pre-commit, list affected files (#31)
Both health and freshness now check for staged .lattice/ changes. If present, the catch-up commit is credited — freshness gap zeroed, tracked files cleared. No more --no-verify bootstrap when adopting the gate. health output now lists the specific lattice-tracked files that triggered the WARN/FAIL verdict (e.g. src/main.rs, src/storage.rs). Fixes #31 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e59b6a4 commit 29d81f1

1 file changed

Lines changed: 106 additions & 33 deletions

File tree

src/main.rs

Lines changed: 106 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3184,6 +3184,15 @@ fn run_command(command: Commands) {
31843184
} => {
31853185
let root = get_lattice_root();
31863186

3187+
// Check if .lattice/ has staged changes (index-aware for pre-commit, #31)
3188+
let lattice_staged = std::process::Command::new("git")
3189+
.args(["diff", "--cached", "--quiet", "--", ".lattice/"])
3190+
.current_dir(&root)
3191+
.output()
3192+
.ok()
3193+
.map(|o| !o.status.success()) // exit 1 = staged changes exist
3194+
.unwrap_or(false);
3195+
31873196
// 1. Freshness: single git call for lattice commit hash + timestamp
31883197
let lattice_git = std::process::Command::new("git")
31893198
.args(["log", "-1", "--format=%H %ct", "--", ".lattice/"])
@@ -3223,9 +3232,14 @@ fn run_command(command: Commands) {
32233232
.parse::<i64>()
32243233
.ok()
32253234
});
3226-
let freshness_gap_hours = match (lattice_ts, code_ts) {
3227-
(Some(l), Some(c)) if c > l => (c - l) as u64 / 3600,
3228-
_ => 0,
3235+
// If .lattice/ has staged changes, this commit IS the catch-up (#31)
3236+
let freshness_gap_hours = if lattice_staged {
3237+
0
3238+
} else {
3239+
match (lattice_ts, code_ts) {
3240+
(Some(l), Some(c)) if c > l => (c - l) as u64 / 3600,
3241+
_ => 0,
3242+
}
32293243
};
32303244

32313245
// 2. Load graph once — derive all metrics from the index
@@ -3264,27 +3278,41 @@ fn run_command(command: Commands) {
32643278
.flat_map(|files| files.iter().map(|f| f.path.clone()))
32653279
.collect();
32663280

3267-
let (tracked_files_changed, total_files_changed) = if !lattice_commit.is_empty() {
3268-
let output = std::process::Command::new("git")
3269-
.args([
3270-
"diff",
3271-
"--name-only",
3272-
&lattice_commit,
3273-
"--",
3274-
".",
3275-
":!.lattice/",
3276-
])
3277-
.current_dir(&root)
3278-
.output()
3279-
.ok()
3280-
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
3281-
.unwrap_or_default();
3282-
let changed: Vec<&str> = output.lines().filter(|l| !l.is_empty()).collect();
3283-
let tracked = changed.iter().filter(|f| bound_files.contains(**f)).count();
3284-
(tracked, changed.len())
3285-
} else {
3286-
(0, 0)
3287-
};
3281+
// If lattice is staged, this commit resolves the diff coupling (#31)
3282+
let (tracked_files_changed, total_files_changed, affected_file_names) =
3283+
if lattice_staged {
3284+
(0, 0, Vec::new())
3285+
} else if !lattice_commit.is_empty() {
3286+
let output = std::process::Command::new("git")
3287+
.args([
3288+
"diff",
3289+
"--name-only",
3290+
&lattice_commit,
3291+
"--",
3292+
".",
3293+
":!.lattice/",
3294+
])
3295+
.current_dir(&root)
3296+
.output()
3297+
.ok()
3298+
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
3299+
.unwrap_or_default();
3300+
let changed: Vec<String> = output
3301+
.lines()
3302+
.filter(|l| !l.is_empty())
3303+
.map(|s| s.to_string())
3304+
.collect();
3305+
let affected: Vec<String> = changed
3306+
.iter()
3307+
.filter(|f| bound_files.contains(f.as_str()))
3308+
.cloned()
3309+
.collect();
3310+
let tracked = affected.len();
3311+
let total = changed.len();
3312+
(tracked, total, affected)
3313+
} else {
3314+
(0, 0, Vec::new())
3315+
};
32883316

32893317
// 4. Lint (strict mode only)
32903318
let lint_issues = if strict {
@@ -3331,7 +3359,9 @@ fn run_command(command: Commands) {
33313359
"total_files_changed": total_files_changed,
33323360
"tracked_files_changed": tracked_files_changed,
33333361
"bound_files_count": bound_files.len(),
3362+
"affected_files": affected_file_names,
33343363
},
3364+
"lattice_staged": lattice_staged,
33353365
});
33363366
if strict {
33373367
result["lint"] = json!({ "issues": lint_issues });
@@ -3346,7 +3376,9 @@ fn run_command(command: Commands) {
33463376
println!("{} {}\n", "HEALTH:".bold(), verdict_colored);
33473377

33483378
println!(" {}", "Freshness:".bold());
3349-
if freshness_gap_hours > 0 {
3379+
if lattice_staged {
3380+
println!(" {}", "Lattice update staged — credited".green());
3381+
} else if freshness_gap_hours > 0 {
33503382
println!(
33513383
" Lattice is {}h behind code changes",
33523384
freshness_gap_hours
@@ -3362,16 +3394,26 @@ fn run_command(command: Commands) {
33623394
println!();
33633395

33643396
println!(" {}", "Code Impact:".bold());
3365-
println!(
3366-
" {} files changed since last lattice update",
3367-
total_files_changed
3368-
);
3369-
if !bound_files.is_empty() {
3397+
if lattice_staged {
3398+
println!(
3399+
" {}",
3400+
"Lattice update staged — diff coupling cleared".green()
3401+
);
3402+
} else {
33703403
println!(
3371-
" {} of {} lattice-tracked files affected",
3372-
tracked_files_changed,
3373-
bound_files.len()
3404+
" {} files changed since last lattice update",
3405+
total_files_changed
33743406
);
3407+
if !bound_files.is_empty() {
3408+
println!(
3409+
" {} of {} lattice-tracked files affected",
3410+
tracked_files_changed,
3411+
bound_files.len()
3412+
);
3413+
}
3414+
for f in &affected_file_names {
3415+
println!(" {}", f.yellow());
3416+
}
33753417
}
33763418

33773419
if strict {
@@ -3488,6 +3530,37 @@ fn run_command(command: Commands) {
34883530
format,
34893531
} => {
34903532
let root = get_lattice_root();
3533+
3534+
// Index-aware: if .lattice/ has staged changes, credit this commit (#31)
3535+
let lattice_staged = std::process::Command::new("git")
3536+
.args(["diff", "--cached", "--quiet", "--", ".lattice/"])
3537+
.current_dir(&root)
3538+
.output()
3539+
.ok()
3540+
.map(|o| !o.status.success())
3541+
.unwrap_or(false);
3542+
3543+
if lattice_staged {
3544+
if is_json(&format) {
3545+
println!(
3546+
"{}",
3547+
serde_json::to_string_pretty(&json!({
3548+
"stale": false,
3549+
"lattice_staged": true,
3550+
"gap_hours": 0,
3551+
"threshold_hours": threshold,
3552+
}))
3553+
.unwrap()
3554+
);
3555+
} else {
3556+
println!(
3557+
"{}",
3558+
"Fresh: lattice update staged — credited for this commit".green()
3559+
);
3560+
}
3561+
return;
3562+
}
3563+
34913564
// Get the most recent commit timestamp touching .lattice/ files
34923565
let lattice_ts = std::process::Command::new("git")
34933566
.args(["log", "-1", "--format=%ct", "--", ".lattice/"])

0 commit comments

Comments
 (0)