huskies: merge 1050

This commit is contained in:
dave
2026-05-14 17:27:42 +00:00
parent 0d3c5579da
commit 8f99fede34
3 changed files with 92 additions and 7 deletions
+5 -3
View File
@@ -1,4 +1,4 @@
//! CLI binary that regenerates `.huskies/source-map.json` from scratch.
//! CLI binary for manual regeneration of `.huskies/source-map.json`.
//!
//! Usage: `source-map-regen [--project-root <path>]`
//!
@@ -6,8 +6,10 @@
//! extracts public item signatures, and writes a fresh sorted JSON map. The output
//! is byte-identical across runs on the same source tree (deterministic).
//!
//! Intended to be called from the pre-commit quality gate (`script/check`) so that
//! every commit captures an accurate, stale-entry-free snapshot of the source map.
//! The pre-commit gate (`script/check`) no longer calls this binary directly — map
//! regeneration is now inlined into the coder spawn path (`local_prompt.rs`) so every
//! agent session starts with a fresh snapshot. This binary is kept as an escape hatch
//! for manual out-of-band regeneration (e.g. after bulk refactors outside the pipeline).
use source_map_gen::regenerate_source_map;
use std::path::Path;
-4
View File
@@ -13,9 +13,5 @@ cargo fmt --manifest-path "$PROJECT_ROOT/Cargo.toml" --all --check
echo "=== Running cargo clippy ==="
cargo clippy --manifest-path "$PROJECT_ROOT/Cargo.toml" --workspace --all-targets -- -D warnings
echo "=== Regenerating source map ==="
cargo run --manifest-path "$PROJECT_ROOT/Cargo.toml" -p source-map-gen --bin source-map-regen --quiet -- --project-root "$PROJECT_ROOT"
git -C "$PROJECT_ROOT" add .huskies/source-map.json
echo "=== Checking doc coverage on changed files ==="
cargo run --manifest-path "$PROJECT_ROOT/Cargo.toml" -p source-map-gen --bin source-map-check --quiet -- --worktree "$PROJECT_ROOT" --base master
+87
View File
@@ -60,6 +60,13 @@ pub fn read_project_local_prompt(project_root: &Path) -> Option<String> {
sections.push((rel_path, trimmed.to_string()));
}
// Regenerate the source map so agents always start from a fresh snapshot.
// Failure is non-fatal: log it and fall through to whatever is on disk.
let map_path = project_root.join(SOURCE_MAP_REL);
if let Err(e) = source_map_gen::regenerate_source_map(project_root, &map_path) {
crate::slog!("[agents] source-map regen failed (non-fatal): {}", e);
}
// Read source-map.json (after AGENT.md) with a byte cap.
let source_map_content = read_source_map_section(project_root);
@@ -387,6 +394,86 @@ mod tests {
);
}
// ── Regen-on-spawn tests ─────────────────────────────────────────────────
fn init_git_repo(dir: &Path) {
let run = |args: &[&str]| {
std::process::Command::new("git")
.args(args)
.current_dir(dir)
.output()
.unwrap();
};
run(&["init"]);
run(&["config", "user.email", "test@test.com"]);
run(&["config", "user.name", "Test"]);
run(&["commit", "--allow-empty", "-m", "init"]);
}
/// Happy path: regen runs successfully and the fresh map is included in the bundle.
#[test]
fn regen_creates_map_on_coder_spawn() {
let tmp = tempfile::tempdir().unwrap();
init_git_repo(tmp.path());
// Write a tracked Rust file so git ls-files has something to index.
write_file(
tmp.path(),
"lib.rs",
"//! Module doc.\n\n/// A function.\npub fn hello() {}\n",
);
std::process::Command::new("git")
.args(["add", "lib.rs"])
.current_dir(tmp.path())
.output()
.unwrap();
std::process::Command::new("git")
.args(["commit", "-m", "add lib.rs"])
.current_dir(tmp.path())
.output()
.unwrap();
// Write an orientation file so we get Some back.
write_file(tmp.path(), "CLAUDE.md", "agent hints");
// Map does not exist yet.
let map_path = tmp.path().join(SOURCE_MAP_REL);
assert!(!map_path.exists(), "map must not exist before spawn");
let result = read_project_local_prompt(tmp.path());
assert!(
result.is_some(),
"bundle must be Some when CLAUDE.md present"
);
// Regen should have written the map.
assert!(
map_path.exists(),
"regen must have written source-map.json during spawn"
);
}
/// Fallback: regen fails (no git repo) but a stale map on disk is still read.
#[test]
fn regen_fails_stale_map_still_readable() {
let tmp = tempfile::tempdir().unwrap();
// No git repo — regen will fail with "git ls-files" error.
write_file(tmp.path(), "CLAUDE.md", "agent hints");
// Write a stale map manually.
write_file(
tmp.path(),
SOURCE_MAP_REL,
r#"{"stale/entry.rs": ["fn old"]}"#,
);
let result = read_project_local_prompt(tmp.path()).unwrap();
assert!(
result.contains("stale/entry.rs"),
"stale map must still be readable after regen failure: {result}"
);
}
#[test]
#[allow(clippy::string_slice)] // sm_start is derived from str::find — always a char boundary
fn source_map_truncated_at_byte_cap() {