Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ model:

Place organization-specific merge rules in `.postil/guardrails.md`. Place additions to the built-in prose policy in `.postil/content-policy.md`. Repository policy extends the built-in content policy unless `contentPolicy.enabled` is false.

Ignored paths are excluded before review planning, so generated artifacts do not consume model context or bounded-review coverage.

## Environment

| Variable | Purpose |
Expand Down
12 changes: 12 additions & 0 deletions src/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2429,7 +2429,15 @@ fn read_length_prefixed(file: &mut File, context: &str) -> Result<Option<String>
}

pub fn prepare_review(snapshot: &DiffSnapshot) -> Result<PreparedReview> {
prepare_review_with_ignore(snapshot, &[])
}

pub fn prepare_review_with_ignore(
snapshot: &DiffSnapshot,
ignore_patterns: &[String],
) -> Result<PreparedReview> {
let text = snapshot.as_str();
let ignore = crate::filter::build_ignore_set(ignore_patterns)?;
let mut windows = tempfile::tempfile().context("creating review-window spool")?;
let mut window_lease = WorkspaceLease::new(snapshot.workspace_budget());
let mut index = DiffIndex::default();
Expand Down Expand Up @@ -2462,6 +2470,10 @@ pub fn prepare_review(snapshot: &DiffSnapshot) -> Result<PreparedReview> {
let end = next_diff_start(text, cursor + "diff --git ".len()).unwrap_or(text.len());
let section = &text[cursor..end];
let path = section_path(section).context("review section has an invalid path header")?;
if ignore.is_match(&path) {
cursor = end;
continue;
}
if is_known_lockfile(&path)
&& let Some(evidence) = lockfile_evidence(&path, section)
{
Expand Down
2 changes: 1 addition & 1 deletion src/review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1125,7 +1125,7 @@ async fn review_diff(cfg: &Config, args: &ReviewArgs, input: ReviewInput<'_>) ->
repository_source,
} = input;
let review_started = std::time::Instant::now();
let mut prepared = diff::prepare_review(diff_snapshot)?;
let mut prepared = diff::prepare_review_with_ignore(diff_snapshot, &cfg.ignore)?;
let input_incomplete = prepared.reserved_anchor;
let mut index = std::mem::take(&mut prepared.index);
let incremental = matches!(scope, filter::ReconcileScope::Incremental { .. });
Expand Down
38 changes: 38 additions & 0 deletions tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2210,6 +2210,44 @@ async fn generated_named_source_is_not_omitted_from_review() {
assert!(body.contains("eval(userInput)"));
}

#[tokio::test]
async fn ignored_paths_are_removed_before_review_planning() {
let server = MockServer::start().await;
mock_review(&server, json!([])).await;

let dir = tempfile::tempdir().unwrap();
let diff = dir.path().join("ignored-source.diff");
std::fs::write(
&diff,
"diff --git a/generated/snapshot.json b/generated/snapshot.json\n--- a/generated/snapshot.json\n+++ b/generated/snapshot.json\n@@ -0,0 +1 @@\n+generated_snapshot_payload\ndiff --git a/src/live.rs b/src/live.rs\n--- a/src/live.rs\n+++ b/src/live.rs\n@@ -0,0 +1 @@\n+validate_live_path();\n",
)
.unwrap();
let config = dir.path().join("postil.yml");
std::fs::write(&config, "ignore:\n - \"generated/**\"\n").unwrap();

let out = postil()
.current_dir(dir.path())
.env("POSTIL_API_BASE", server.uri())
.env("POSTIL_DISABLE_SCORER", "1")
.args(["review", "--diff-file"])
.arg(&diff)
.arg("--config")
.arg(&config)
.args(["--output", "json"])
.assert()
.success();

let envelope: Value = serde_json::from_slice(&out.get_output().stdout).unwrap();
assert!(envelope["findings"].as_array().unwrap().is_empty());
let requests = server.received_requests().await.unwrap();
assert_eq!(requests.len(), 1);
let body = String::from_utf8_lossy(&requests[0].body);
assert!(body.contains("src/live.rs"));
assert!(body.contains("validate_live_path"));
assert!(!body.contains("generated/snapshot.json"));
assert!(!body.contains("generated_snapshot_payload"));
}

#[tokio::test]
async fn oversized_security_hunk_fails_before_provider_contact() {
use std::fmt::Write as _;
Expand Down
Loading