Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
722f100
fix(audit): relocate fixability provider integration test (#8425)
chubes4 Aug 8, 2026
28a2311
test(audit): require provider fixability summary
chubes4 Aug 8, 2026
018f82c
Merge branch fix/11995-adopt-args-test-compile into fix/8425-workspac…
chubes4 Aug 8, 2026
b1d5226
ci: enable differential Test gating [AI: openai/gpt-5.6-sol via OpenC…
chubes4 Aug 9, 2026
57cffd8
fix(test): union changed Rust test identities (#11751)
chubes4 Aug 9, 2026
c6c10f9
test(audit): inventory provider regression (#12000)
chubes4 Aug 9, 2026
5e73c1e
fix(test): materialize bounded Rust selection (#11751)
chubes4 Aug 9, 2026
468ac53
fix(test): version Rust selection artifact contract
chubes4 Aug 9, 2026
fb0db1c
test(rust): cover bounded selection artifact fallback
chubes4 Aug 9, 2026
49536e6
Merge branch fix/11751-rust-changed-test-union into fix/8425-workspac…
chubes4 Aug 9, 2026
12423a3
ci: pin Test workflow bootstrap baseline
chubes4 Aug 9, 2026
07ac8e4
fix(test): require validated inventory-only evidence
chubes4 Aug 9, 2026
e8df7c2
fix(test): bind inventory evidence provenance [AI: openai/gpt-5.6-ter…
chubes4 Aug 9, 2026
fd96cb3
fix(test): verify inventory provenance by dirfd [AI: openai/gpt-5.6-t…
chubes4 Aug 9, 2026
1fd0634
fix(test): bind inventory to selected runner
chubes4 Aug 9, 2026
f9fb0c0
fix(test): normalize workspace fingerprint newlines [AI: openai/gpt-5…
chubes4 Aug 9, 2026
ee2220d
fix(test): cfg-gate Unix inventory evidence [AI: openai/gpt-5.6-terra…
chubes4 Aug 9, 2026
e41c3ba
fix(test): gate inventory producer path on Unix [AI: openai/gpt-5.6-t…
chubes4 Aug 9, 2026
61360f3
fix(test): publish validated inventory safely [AI: openai/gpt-5.6-sol…
chubes4 Aug 9, 2026
154236f
Merge main into #12000 integration
chubes4 Aug 9, 2026
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: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ jobs:
component: homeboy
source: .
scope: auto
differential-gating: 'false'
differential-gating: 'true'
baseline-commands: none
test-shards: ${{ needs.ci-capacity-admission.outputs.test-shards }}
execution-timeout-seconds: '1800'
Expand Down
17 changes: 17 additions & 0 deletions crates/contracts/homeboy-extension-contract/src/test_results.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ pub struct TestCommandOutput {
pub failure: Option<PhaseFailure>,
#[serde(skip_serializing_if = "Option::is_none")]
pub test_counts: Option<TestCounts>,
/// Positive evidence emitted by an inventory-only test run. This replaces
/// execution counts only for that explicit mode.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub test_inventory: Option<TestInventoryOutput>,
/// Duration facts for this phase. Deliberately separate from `findings`:
/// those drive failure classification, and a slow test is not a failing
/// test. `None` when nothing could be measured — never a zeroed block.
Expand Down Expand Up @@ -85,6 +89,8 @@ pub struct TestRunWorkflowResult {
pub runner_exit_code: Option<i32>,
pub test_counts: Option<TestCounts>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub test_inventory: Option<TestInventoryOutput>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub test_durations: Option<TestDurations>,
pub findings: Option<Vec<HomeboyFinding>>,
#[serde(skip)]
Expand All @@ -104,6 +110,17 @@ pub struct TestRunWorkflowResult {
pub extension_phase_timings: Vec<ExtensionPhaseTiming>,
}

/// Validated inventory-only test evidence from a supervised extension child.
#[derive(Debug, Clone, Serialize)]
pub struct TestInventoryOutput {
pub schema: String,
pub runner: String,
pub runner_fingerprint: String,
pub workspace_fingerprint: String,
pub test_count: usize,
pub inventory_fingerprint: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct DriftWorkflowResult {
pub component: String,
Expand Down
7 changes: 7 additions & 0 deletions crates/homeboy-cli/src/commands/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1375,6 +1375,7 @@ mod tests {
exit_code: 1,
runner_exit_code: None,
test_counts: None,
test_inventory: None,
test_durations: None,
findings: None,
failure_analysis_input: None,
Expand Down Expand Up @@ -1465,6 +1466,7 @@ mod tests {
exit_code: 1,
runner_exit_code: None,
test_counts: None,
test_inventory: None,
test_durations: None,
findings: None,
failure_analysis_input: Some(input),
Expand Down Expand Up @@ -1556,6 +1558,7 @@ mod tests {
exit_code: 101,
runner_exit_code: None,
test_counts: Some(TestCounts::new(0, 0, 0, 0)),
test_inventory: None,
test_durations: None,
findings: None,
failure_analysis_input: None,
Expand Down Expand Up @@ -1634,6 +1637,7 @@ mod tests {
exit_code: 1,
runner_exit_code: None,
test_counts: None,
test_inventory: None,
test_durations: None,
findings: None,
failure_analysis_input: None,
Expand Down Expand Up @@ -1782,6 +1786,7 @@ mod tests {
},
runner_exit_code: Some(runner_exit_code),
test_counts: None,
test_inventory: None,
test_durations: None,
findings: None,
failure_analysis_input: None,
Expand Down Expand Up @@ -1847,6 +1852,7 @@ mod tests {
exit_code: 1,
runner_exit_code: None,
test_counts: None,
test_inventory: None,
test_durations: None,
findings: None,
failure_analysis_input: None,
Expand Down Expand Up @@ -1935,6 +1941,7 @@ mod tests {
exit_code: 143,
runner_exit_code: None,
test_counts: None,
test_inventory: None,
test_durations: None,
findings: None,
failure_analysis_input: None,
Expand Down
56 changes: 1 addition & 55 deletions crates/homeboy-code-audit/src/report_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,64 +280,10 @@ fn test_compute_fixability_skips_structural_only_results() {
assert!(compute_fixability(&result).is_none());
}

#[cfg(feature = "slow-tests")]
#[test]
fn test_compute_fixability_counts_fixes_from_real_audit() {
use std::fs;

// Fixability planning is inverted behind a provider the CLI registers at
// startup; a core lib test never runs the CLI, so register the refactor-backed
// provider explicitly — otherwise fixability is always unavailable (None).
crate::refactor::audit_fixability_provider::register();

let dir = tempfile::tempdir().expect("temp dir");
let root = dir.path();

// Create a minimal codebase with a detectable convention + outlier
fs::create_dir_all(root.join("commands")).unwrap();
// Two conforming files establish a convention (methods: run + helper)
fs::write(
root.join("commands/good_one.rs"),
"pub fn run() {}\npub fn helper() {}\n",
)
.unwrap();
fs::write(
root.join("commands/good_two.rs"),
"pub fn run() {}\npub fn helper() {}\n",
)
.unwrap();
// One outlier is missing a method → should produce a fixable finding
fs::write(root.join("commands/bad.rs"), "pub fn run() {}\n").unwrap();

// Run a real audit
let result = crate::audit_path_with_id("fixability-test", &root.to_string_lossy())
.expect("audit should run");

// Compute fixability
let fixability = compute_fixability(&result);

// Should have at least some fixable findings (the missing method outlier)
if let Some(fix) = fixability {
assert!(
fix.fixable_count > 0,
"expected at least one fixable finding"
);
// automated + manual_only should equal fixable_count
assert_eq!(
fix.fixable_count,
fix.automated_count + fix.manual_only_count
);
// by_kind should not be empty
assert!(!fix.by_kind.is_empty(), "expected per-kind breakdown");
}
// Note: fixability may also be None if the minimal codebase doesn't trigger
// enough conventions — that's acceptable for this test.
}

// Runs the full audit-with-analysis pipeline (walk + fingerprint + detectors +
// fixability planning) over a fixture tree, matching the broad-machinery slow
// tier described in `docs/internals/test-tiers.md` — the same tier its sibling
// `test_compute_fixability_counts_fixes_from_real_audit` already lives in.
// provider-wiring regression in `homeboy-refactor` lives in.
#[cfg(feature = "slow-tests")]
#[test]
fn test_compute_fixability_with_analysis() {
Expand Down
35 changes: 35 additions & 0 deletions crates/homeboy-extension/src/test/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub fn from_main_workflow_with_ci_context(
phase,
failure,
test_counts: result.test_counts,
test_inventory: result.test_inventory,
// Carried through untouched. `test_phase_report` and
// `test_phase_failure` below never read it: a slow suite must not
// be able to change the phase verdict in either direction. (#10655)
Expand Down Expand Up @@ -123,6 +124,7 @@ pub fn from_drift_workflow(result: DriftWorkflowResult) -> (TestCommandOutput, i
phase: None,
failure: None,
test_counts: None,
test_inventory: None,
test_durations: None,
findings: None,
coverage: None,
Expand Down Expand Up @@ -167,6 +169,7 @@ pub fn from_auto_fix_drift_workflow(
phase: None,
failure: None,
test_counts: None,
test_inventory: None,
test_durations: None,
findings: None,
coverage: None,
Expand Down Expand Up @@ -312,6 +315,7 @@ mod tests {
exit_code: 1,
runner_exit_code: None,
test_counts: Some(TestCounts::new(3, 1, 2, 0)),
test_inventory: None,
test_durations: None,
findings,
failure_analysis_input: None,
Expand All @@ -334,6 +338,7 @@ mod tests {
exit_code,
runner_exit_code: None,
test_counts: Some(counts),
test_inventory: None,
test_durations: None,
findings: None,
failure_analysis_input: None,
Expand All @@ -356,6 +361,7 @@ mod tests {
exit_code: 0,
runner_exit_code: None,
test_counts: Some(TestCounts::new(0, 0, 0, 0)),
test_inventory: None,
test_durations: None,
findings: None,
failure_analysis_input: None,
Expand All @@ -371,6 +377,35 @@ mod tests {
}
}

#[test]
fn serializes_validated_inventory_mode_success() {
let mut workflow = workflow_result(None);
workflow.status = "passed".to_string();
workflow.exit_code = 0;
workflow.runner_exit_code = Some(0);
workflow.test_counts = None;
workflow.test_inventory = Some(
homeboy_extension_contract::test_results::TestInventoryOutput {
schema: "homeboy/test-inventory/v1".to_string(),
runner: "nextest".to_string(),
runner_fingerprint: "a".repeat(64),
workspace_fingerprint: "b".repeat(64),
test_count: 4,
inventory_fingerprint: "c".repeat(64),
},
);

let (output, exit_code) = from_main_workflow(workflow);
let rendered = serde_json::to_value(output).expect("inventory output serializes");

assert_eq!(exit_code, 0);
assert_eq!(
rendered["test_inventory"]["schema"],
"homeboy/test-inventory/v1"
);
assert_eq!(rendered["test_inventory"]["test_count"], 4);
}

#[test]
fn serializes_findings_when_present() {
let (output, exit_code) =
Expand Down
Loading
Loading