From 8ea814850d3e4f3c9a52a12cf8e7a07fede7fbef Mon Sep 17 00:00:00 2001 From: t Date: Sun, 25 Oct 2026 08:19:41 +0100 Subject: [PATCH 1/2] chore(roadmap): entries for PMAT-5446, PMAT-5909 and PMAT-5803 so #6294 and #6309 split by ticket PMAT-5446 covers the #5893 planted-.pv tests carried by #6309 (issue #5446: slow contracts-cli ont4 gate tests); PMAT-5909 and PMAT-5803 take their scope from #6294. Entries plus the regenerated aggregate only. PMAT-Ticket: PMAT-5446 Co-Authored-By: Claude Sonnet 5.5 --- docs/roadmaps/entries/PMAT-5446.yaml | 22 +++++++++ docs/roadmaps/entries/PMAT-5803.yaml | 23 ++++++++++ docs/roadmaps/entries/PMAT-5909.yaml | 23 ++++++++++ docs/roadmaps/roadmap.yaml | 68 ++++++++++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 docs/roadmaps/entries/PMAT-5446.yaml create mode 100644 docs/roadmaps/entries/PMAT-5803.yaml create mode 100644 docs/roadmaps/entries/PMAT-5909.yaml diff --git a/docs/roadmaps/entries/PMAT-5446.yaml b/docs/roadmaps/entries/PMAT-5446.yaml new file mode 100644 index 0000000000..50606c00b7 --- /dev/null +++ b/docs/roadmaps/entries/PMAT-5446.yaml @@ -0,0 +1,22 @@ +- id: PMAT-5446 + github_issue: 5446 + item_type: task + title: 'ci: shrink the slow quick-tier tests (apr-cli test_llm_band, contracts-cli ont4 gate tests) at the root' + status: in_progress + priority: medium + assigned_to: null + created: 2026-10-25T07:50:00Z + updated: 2026-10-25T07:50:00Z + spec: null + acceptance_criteria: + - 'contracts-cli ont4_relations_gate, ont4b_shapes_gate and ont5_consistency_gate lint a planted fixture or copied files, not the full contracts tree, and each finishes well under 60 s (landed by #5893 via the EMR-001 integration PR #6309)' + - the pv-sat corpus() lint tests use a planted .pv fixture (same change) + - apr-cli commands::test_llm_band tests no longer take 60-240 s each (separate PR; not covered by #6309) + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: |- + Entry minted for the #5893 planted-.pv tests carried by #6309 (quorum handoff triage-quorum-tickets-6294-6309-0718Z). Issue #5446 names both slow families; #5893 is the contracts-cli half (gate tests lint a fixture, 11/11 per its body). The test_llm_band half has no PR yet, so the item stays in_progress. + epic: null diff --git a/docs/roadmaps/entries/PMAT-5803.yaml b/docs/roadmaps/entries/PMAT-5803.yaml new file mode 100644 index 0000000000..122a4e256e --- /dev/null +++ b/docs/roadmaps/entries/PMAT-5803.yaml @@ -0,0 +1,23 @@ +- id: PMAT-5803 + github_issue: 5803 + item_type: task + title: 'build: FLOW-010 cut 1 — move the tuner tests that need aprender-core out of aprender-compute so compute drops its dev-dependency on core' + status: in_progress + priority: medium + assigned_to: null + created: 2026-10-25T07:50:00Z + updated: 2026-10-25T07:50:00Z + spec: null + acceptance_criteria: + - the tuner training-data-quality tests move from crates/aprender-compute/tests/tuner_falsification to crates/aprender-core/tests/tuner_training_data_quality.rs and still run (ci/explicit-test-commands.d fragments 295 and 298 name them) + - aprender-compute has no dev-dependency on aprender-core + - scripts/check_tuner_test_move.sh proves the move lost no test (count and names before and after) + - one PR, at most 400 lines or a recorded PMAT-Size-Waiver (the move was 810 lines in #6294 commit ebf773587a, waiver sanctioned by quorum triage-quorum-door6261-conditions-2136Z) + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: |- + Scope taken from #6294 (commit 2, PMAT-5803; issue #5803, FLOW-010 five-whys on crate fan-out, cut 1). Entry minted so w5032 can cut this piece as its own PR. + epic: null diff --git a/docs/roadmaps/entries/PMAT-5909.yaml b/docs/roadmaps/entries/PMAT-5909.yaml new file mode 100644 index 0000000000..2563051515 --- /dev/null +++ b/docs/roadmaps/entries/PMAT-5909.yaml @@ -0,0 +1,23 @@ +- id: PMAT-5909 + github_issue: 5909 + item_type: task + title: 'build: split env_key out of aprender-contracts into aprender-contracts-key so a contracts edit stops dirtying 47 of 85 crates' + status: in_progress + priority: medium + assigned_to: null + created: 2026-10-25T07:50:00Z + updated: 2026-10-25T07:50:00Z + spec: null + acceptance_criteria: + - a new crate crates/aprender-contracts-key holds env_key (src/lib.rs, README, Cargo.toml) and aprender-contracts re-uses it through build_helper + - the build.rs and Cargo.toml of each dependent crate (aprender-core, -compute, -db, -orchestrate, -present-cli, -present-core, -rag, -serve, -simulate, -train, -viz-ttop) depend on aprender-contracts-key instead of aprender-contracts + - cargo metadata --locked is clean and the crate is registered in scripts/release/publish-order.txt and scripts/cascade-publish.sh; publishing it stays on the release path + - one PR, at most 400 lines, no Sensitive-path change beyond the workspace member and publish order lines (cut from #6294 commit 4b8f637715) + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: |- + Scope taken from #6294 (commit 1, PMAT-5909; issue #5909: aprender-contracts is 24 commits in 30 days and dirties 47 of 85 crates). #6294 is split by quorum triage-quorum-tickets-6294-6309-0718Z into one PR per ticket; this entry lets w5032 cut this piece. + epic: null diff --git a/docs/roadmaps/roadmap.yaml b/docs/roadmaps/roadmap.yaml index b5bd1bc998..5f1bb142f2 100644 --- a/docs/roadmaps/roadmap.yaml +++ b/docs/roadmaps/roadmap.yaml @@ -25319,6 +25319,28 @@ Not done, pending the ruling: moving the 31 fixture files to an obviously synthe - kind:code notes: null epic: 4780 +- id: PMAT-5446 + github_issue: 5446 + item_type: task + title: 'ci: shrink the slow quick-tier tests (apr-cli test_llm_band, contracts-cli ont4 gate tests) at the root' + status: in_progress + priority: medium + assigned_to: null + created: 2026-10-25T07:50:00Z + updated: 2026-10-25T07:50:00Z + spec: null + acceptance_criteria: + - 'contracts-cli ont4_relations_gate, ont4b_shapes_gate and ont5_consistency_gate lint a planted fixture or copied files, not the full contracts tree, and each finishes well under 60 s (landed by #5893 via the EMR-001 integration PR #6309)' + - the pv-sat corpus() lint tests use a planted .pv fixture (same change) + - apr-cli commands::test_llm_band tests no longer take 60-240 s each (separate PR; not covered by #6309) + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: |- + Entry minted for the #5893 planted-.pv tests carried by #6309 (quorum handoff triage-quorum-tickets-6294-6309-0718Z). Issue #5446 names both slow families; #5893 is the contracts-cli half (gate tests lint a fixture, 11/11 per its body). The test_llm_band half has no PR yet, so the item stays in_progress. + epic: null - id: PMAT-5463 github_issue: 5463 item_type: task @@ -25375,6 +25397,29 @@ Not done, pending the ruling: moving the 31 fixture files to an obviously synthe - kind:code notes: null epic: 3863 +- id: PMAT-5803 + github_issue: 5803 + item_type: task + title: 'build: FLOW-010 cut 1 — move the tuner tests that need aprender-core out of aprender-compute so compute drops its dev-dependency on core' + status: in_progress + priority: medium + assigned_to: null + created: 2026-10-25T07:50:00Z + updated: 2026-10-25T07:50:00Z + spec: null + acceptance_criteria: + - the tuner training-data-quality tests move from crates/aprender-compute/tests/tuner_falsification to crates/aprender-core/tests/tuner_training_data_quality.rs and still run (ci/explicit-test-commands.d fragments 295 and 298 name them) + - aprender-compute has no dev-dependency on aprender-core + - scripts/check_tuner_test_move.sh proves the move lost no test (count and names before and after) + - one PR, at most 400 lines or a recorded PMAT-Size-Waiver (the move was 810 lines in #6294 commit ebf773587a, waiver sanctioned by quorum triage-quorum-door6261-conditions-2136Z) + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: |- + Scope taken from #6294 (commit 2, PMAT-5803; issue #5803, FLOW-010 five-whys on crate fan-out, cut 1). Entry minted so w5032 can cut this piece as its own PR. + epic: null - id: PMAT-5817 github_issue: 5817 item_type: task @@ -25550,6 +25595,29 @@ Not done, pending the ruling: moving the 31 fixture files to an obviously synthe - kind:measurement notes: null epic: 5510 +- id: PMAT-5909 + github_issue: 5909 + item_type: task + title: 'build: split env_key out of aprender-contracts into aprender-contracts-key so a contracts edit stops dirtying 47 of 85 crates' + status: in_progress + priority: medium + assigned_to: null + created: 2026-10-25T07:50:00Z + updated: 2026-10-25T07:50:00Z + spec: null + acceptance_criteria: + - a new crate crates/aprender-contracts-key holds env_key (src/lib.rs, README, Cargo.toml) and aprender-contracts re-uses it through build_helper + - the build.rs and Cargo.toml of each dependent crate (aprender-core, -compute, -db, -orchestrate, -present-cli, -present-core, -rag, -serve, -simulate, -train, -viz-ttop) depend on aprender-contracts-key instead of aprender-contracts + - cargo metadata --locked is clean and the crate is registered in scripts/release/publish-order.txt and scripts/cascade-publish.sh; publishing it stays on the release path + - one PR, at most 400 lines, no Sensitive-path change beyond the workspace member and publish order lines (cut from #6294 commit 4b8f637715) + phases: [] + subtasks: [] + estimated_effort: null + labels: + - kind:code + notes: |- + Scope taken from #6294 (commit 1, PMAT-5909; issue #5909: aprender-contracts is 24 commits in 30 days and dirties 47 of 85 crates). #6294 is split by quorum triage-quorum-tickets-6294-6309-0718Z into one PR per ticket; this entry lets w5032 cut this piece. + epic: null - id: PMAT-5914 github_issue: 5914 item_type: task From b14da3d9e0f2a565f78fe3f166b07268d8fbea29 Mon Sep 17 00:00:00 2001 From: t Date: Sun, 25 Oct 2026 08:25:04 +0100 Subject: [PATCH 2/2] test(tuner): RandomForest tuner training-data-quality tests land in aprender-core (Refs #5803) Adds the moved tests and their explicit-test command (pure add, +172). Part 1 of 3; the deletes are part 2. Piece of the #6336 split (aprender-03: size over the cap, split; no waiver), itself a piece of the #6294 split. Stacked on #6334 (PMAT-5803 entry). PMAT-Ticket: PMAT-5803 Co-Authored-By: Claude Sonnet 5.5 --- ...ender-core-tuner-training-data-quality.cmd | 1 + .../tests/tuner_training_data_quality.rs | 171 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 ci/explicit-test-commands.d/295-aprender-core-tuner-training-data-quality.cmd create mode 100644 crates/aprender-core/tests/tuner_training_data_quality.rs diff --git a/ci/explicit-test-commands.d/295-aprender-core-tuner-training-data-quality.cmd b/ci/explicit-test-commands.d/295-aprender-core-tuner-training-data-quality.cmd new file mode 100644 index 0000000000..c69214adf3 --- /dev/null +++ b/ci/explicit-test-commands.d/295-aprender-core-tuner-training-data-quality.cmd @@ -0,0 +1 @@ +cargo test -p aprender-core --test tuner_training_data_quality diff --git a/crates/aprender-core/tests/tuner_training_data_quality.rs b/crates/aprender-core/tests/tuner_training_data_quality.rs new file mode 100644 index 0000000000..41960a5b1f --- /dev/null +++ b/crates/aprender-core/tests/tuner_training_data_quality.rs @@ -0,0 +1,171 @@ +#![allow(clippy::disallowed_methods, clippy::float_cmp)] +//! F041-F060 (aprender RandomForest half): Training Data Quality, ML-tuner showcase. +//! +//! SHOWCASE-BRICK-001 feeds trueno `TunerFeatures` into `aprender::RandomForestRegressor` +//! and `RandomForestClassifier`. These tests lived in aprender-compute behind a +//! `ml-tuner` feature nothing enabled, driven by a dev-dependency of compute on core. +//! That dev edge tied compute (the base of the stack) to core, so a touch of any base +//! crate selected most of the workspace (FLOW-010, #5803). Core depends on compute, so a +//! change to compute still selects these tests here. The tests that need no aprender stay +//! in `crates/aprender-compute/tests/tuner_falsification/training_data_quality.rs`. + +use trueno::tuner::TunerFeatures; + +/// Train an aprender RandomForest regressor on trueno features, with the showcase's +/// `<10 samples` guard. Returns `Err` on insufficient data or matrix/fit failure. +fn fit_throughput_rf( + rows: &[(TunerFeatures, f32)], + n_trees: usize, +) -> Result { + use aprender::primitives::{Matrix, Vector}; + use aprender::tree::RandomForestRegressor; + if rows.len() < 10 { + return Err(format!("insufficient data: {} < 10", rows.len())); + } + let dim = TunerFeatures::DIM; + let n = rows.len(); + let mut x = Vec::with_capacity(n * dim); + let mut y = Vec::with_capacity(n); + for (f, t) in rows { + x.extend(f.to_vector()); + y.push(*t); + } + let x_mat = Matrix::from_vec(n, dim, x).map_err(|e| e.to_string())?; + let mut rf = RandomForestRegressor::new(n_trees); + rf.fit(&x_mat, &Vector::from_vec(y)) + .map_err(|e| e.to_string())?; + Ok(rf) +} + +/// F041: Empty training data should error +#[test] +fn f041_empty_training_errors() { + let empty_data: Vec<(TunerFeatures, f32)> = vec![]; + let result = fit_throughput_rf(&empty_data, 10); + assert!( + result.is_err(), + "F041 FALSIFIED: empty training data should error" + ); +} + +/// F042: Single sample training should error gracefully (below the 10-sample floor) +#[test] +fn f042_single_sample_graceful() { + let features = TunerFeatures::builder().model_params_b(1.5).build(); + let data = vec![(features, 100.0)]; + // Should error (insufficient), not panic + let _ = fit_throughput_rf(&data, 10); +} + +/// F043: Training with NaN labels should not produce NaN predictions +#[test] +fn f043_nan_labels_error() { + use aprender::primitives::Matrix; + let mut data: Vec<(TunerFeatures, f32)> = (0..10) + .map(|i| { + ( + TunerFeatures::builder() + .batch_size(1 + (i % 8) as u32) + .build(), + 100.0, + ) + }) + .collect(); + data[0].1 = f32::NAN; + if let Ok(rf) = fit_throughput_rf(&data, 10) { + let fx = Matrix::from_vec(1, TunerFeatures::DIM, data[1].0.to_vector().to_vec()).unwrap(); + let pred = rf.predict(&fx); + assert!( + pred.as_slice()[0].is_finite() || pred.as_slice()[0].is_nan(), + "F043: prediction must be a well-defined float" + ); + } +} + +/// F044: Training with negative labels should be handled without panic +#[test] +fn f044_negative_labels_handled() { + let data: Vec<(TunerFeatures, f32)> = (0..12) + .map(|i| { + let f = TunerFeatures::builder() + .batch_size(1 + (i % 8) as u32) + .build(); + (f, if i % 2 == 0 { -100.0 } else { 100.0 }) + }) + .collect(); + // Must not panic; result may be Ok or Err. + let _ = fit_throughput_rf(&data, 10); +} + +/// F046: Training improves over heuristic (or doesn't regress) +#[test] +fn f046_training_improves() { + // Generate training data that matches heuristic pattern + let training_data: Vec<(TunerFeatures, f32)> = (0..50) + .map(|i| { + let batch = 1 + (i % 8) as u32; + let features = TunerFeatures::builder() + .model_params_b(1.5) + .batch_size(batch) + .gpu_mem_bw_gbs(1000.0) + .build(); + // Throughput scales with batch size + let throughput = 100.0 + (batch as f32) * 50.0; + (features, throughput) + }) + .collect(); + + let result = fit_throughput_rf(&training_data, 50); + assert!( + result.is_ok(), + "F046 FALSIFIED: training failed: {:?}", + result.err() + ); +} + +/// F047: Large training set should not OOM +#[test] +fn f047_large_training_no_oom() { + let training_data: Vec<(TunerFeatures, f32)> = (0..1000) + .map(|i| { + let features = TunerFeatures::builder() + .model_params_b((i % 10) as f32 * 0.5 + 0.5) + .batch_size((i % 8 + 1) as u32) + .build(); + (features, 100.0 + (i as f32)) + }) + .collect(); + + let result = fit_throughput_rf(&training_data, 10); + assert!(result.is_ok(), "F047 FALSIFIED: large training failed"); +} + +/// F048: Classifier training should work (aprender RandomForestClassifier on trueno features) +#[test] +fn f048_classifier_training() { + use aprender::primitives::Matrix; + use aprender::tree::RandomForestClassifier; + + let dim = TunerFeatures::DIM; + let n = 50usize; + let mut x = Vec::with_capacity(n * dim); + let mut y: Vec = Vec::with_capacity(n); + for i in 0..n { + let batch = 1 + (i % 8) as u32; + let f = TunerFeatures::builder() + .model_params_b(1.5) + .batch_size(batch) + .build(); + x.extend(f.to_vector()); + // Label: BatchedQ4K (3) for M>=4, VectorizedQ4K (2) otherwise + y.push(if batch >= 4 { 3 } else { 2 }); + } + let x_mat = Matrix::from_vec(n, dim, x).expect("feature matrix"); + let mut classifier = RandomForestClassifier::new(10); + let result = classifier.fit(&x_mat, &y); + assert!( + result.is_ok(), + "F048 FALSIFIED: classifier training failed: {:?}", + result.err() + ); +}