Skip to content

Commit de8388f

Browse files
gmoonclaude
andcommitted
health --strict: FAIL on diff-coupled staleness, not wall-clock (#30)
In strict mode, tracked files changed without a lattice update now escalates to FAIL. Lone wall-clock staleness stays WARN — it belongs at branch/repo altitude (CI, pre-push), not on individual commits. Freshness threshold now configurable via config.yaml (freshness_threshold_hours, default 72). Fixes #30 Bump version to v0.2.3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bc14eb7 commit de8388f

7 files changed

Lines changed: 50 additions & 23 deletions

File tree

.githooks/pre-commit

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
# Forkzero standard pre-commit gate. See docs/ENGINEERING.md in the forkzero workspace.
3+
make pre-commit

.githooks/pre-push

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/bin/sh
2+
# Forkzero standard pre-push gate. See docs/ENGINEERING.md in the forkzero workspace.
3+
make pre-push

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "lattice"
3-
version = "0.2.2"
3+
version = "0.2.3"
44
edition = "2024"
55
description = "A knowledge coordination protocol for human-agent collaboration"
66
repository = "https://github.com/forkzero/lattice"

Makefile

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: fmt lint test test-all check build clean install docker-e2e pre-commit pre-push
1+
.PHONY: fmt fmt-check format format-check lint test test-all check build clean install docker-e2e pre-commit pre-push lattice-fresh hooks
22

33
# Format code
44
fmt:
@@ -8,6 +8,10 @@ fmt:
88
fmt-check:
99
cargo fmt --check
1010

11+
# Cross-repo verb aliases (every forkzero repo exposes format / format-check)
12+
format: fmt
13+
format-check: fmt-check
14+
1115
# Run clippy lints
1216
lint:
1317
cargo clippy --all-targets --all-features -- -D warnings
@@ -29,10 +33,24 @@ pre-commit: fmt-check lint
2933
fi
3034
@echo "Pre-commit checks passed."
3135

32-
# Pre-push gate: full checks (format + lint + test + build)
33-
pre-push: pre-commit test build
36+
# Lattice staleness gate: hard-fail if .lattice is >72h behind code.
37+
# Time-based threshold lives at push/CI altitude, NOT pre-commit (see docs/ENGINEERING.md).
38+
lattice-fresh:
39+
@if command -v lattice >/dev/null 2>&1; then \
40+
lattice freshness --check || { echo "Lattice is stale (>72h behind code). Update .lattice before pushing."; exit 1; }; \
41+
else \
42+
echo "Note: lattice freshness skipped (lattice not installed)"; \
43+
fi
44+
45+
# Pre-push gate: full checks (format + lint + lattice freshness + test + build)
46+
pre-push: pre-commit lattice-fresh test build
3447
@echo "Pre-push checks passed."
3548

49+
# Install git hooks (Rust repo has no husky — wire core.hooksPath to committed .githooks/)
50+
hooks:
51+
git config core.hooksPath .githooks
52+
@echo "Git hooks installed (core.hooksPath=.githooks)."
53+
3654
# Legacy alias
3755
check: fmt lint test
3856
@echo "All checks passed!"

src/main.rs

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3280,33 +3280,34 @@ fn run_command(command: Commands) {
32803280
(0, 0)
32813281
};
32823282

3283-
// Compute verdict:
3284-
// FAIL: pressure + code/time signal together, or high pressure alone
3285-
// WARN: any single signal (code changed, stale, or some pressure)
3286-
// PASS: nothing flagged
3287-
let verdict = if (change_pressure > 0
3288-
&& (tracked_files_changed > 0 || freshness_gap_hours > 72))
3289-
|| change_pressure > 3
3290-
{
3291-
"FAIL"
3292-
} else if tracked_files_changed > 0 || freshness_gap_hours > 72 || change_pressure > 0 {
3293-
"WARN"
3294-
} else {
3295-
"PASS"
3296-
};
3297-
32983283
// 4. Lint (strict mode only)
32993284
let lint_issues = if strict {
33003285
lint_lattice(&root).issues.len()
33013286
} else {
33023287
0
33033288
};
33043289

3305-
// Escalate verdict if lint issues found in strict mode
3306-
let verdict = if strict && lint_issues > 0 {
3290+
// Read configurable threshold
3291+
let config = load_config(&root);
3292+
let threshold = config.freshness_threshold_hours.unwrap_or(72);
3293+
3294+
// Compute verdict:
3295+
// Strict mode: FAIL on diff-coupled staleness (bound files changed without lattice update)
3296+
// and lint issues. Wall-clock staleness alone stays WARN.
3297+
// Normal mode: FAIL when pressure + signals combine. WARN on any single signal.
3298+
let verdict = if (strict && (lint_issues > 0 || tracked_files_changed > 0))
3299+
|| (change_pressure > 0
3300+
&& (tracked_files_changed > 0 || freshness_gap_hours > threshold))
3301+
|| change_pressure > 3
3302+
{
33073303
"FAIL"
3304+
} else if tracked_files_changed > 0
3305+
|| freshness_gap_hours > threshold
3306+
|| change_pressure > 0
3307+
{
3308+
"WARN"
33083309
} else {
3309-
verdict
3310+
"PASS"
33103311
};
33113312

33123313
if is_json(&format) {

src/storage.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ pub struct LatticeConfig {
124124
pub api_key: Option<String>,
125125
#[serde(default, skip_serializing_if = "Option::is_none")]
126126
pub schema_version: Option<String>,
127+
#[serde(default, skip_serializing_if = "Option::is_none")]
128+
pub freshness_threshold_hours: Option<u64>,
127129
}
128130

129131
/// The current schema version. Bump when .lattice/ format changes.

0 commit comments

Comments
 (0)