Sweep every first-party crate source (1956 .rs files) to the project comment guidelines: delete redundant restatements, decorative banners, change narration, and end-of-line comments; keep and tighten the crucial ones (invariants, bug rationale, SAFETY blocks, ported-source attribution). No functional code changed. Every edit is proven comment-only against the prior tree by a comment-stripping lexer (string/char/raw-string aware) plus a separate doctest-fence check. Where removing a comment made rustfmt or clippy want to re-lay-out adjacent code, the minimal triggering comment is restored so code tokens stay byte-identical. Gates green: cargo fmt --all --check (0 diffs), cargo check and cargo clippy --workspace --all-targets (0 warnings). Adds scripts/check_codegen_comment_guidelines.py — the enforcement gate for these guidelines (flags banners, end-of-line comments, change narration, and commented-out code).
119 lines
4.5 KiB
Rust
119 lines
4.5 KiB
Rust
//! summary.json reasoning-effort persistence tests.
|
|
//!
|
|
//! Regression tests for "what effort was this session run on?": a fresh
|
|
//! session must record its resolved `reasoning_effort` in `summary.json` at
|
|
//! creation — not only after an explicit model/effort switch (the old
|
|
//! behavior, which left the field absent for sessions that never switched).
|
|
//!
|
|
//! Each test spawns a real `kigi agent stdio` process against a mock
|
|
//! inference server and asserts on the persisted `summary.json`.
|
|
//!
|
|
//! Run locally:
|
|
//! ```bash
|
|
//! cargo test -p kigi-shell --test test_summary_reasoning_effort -- --ignored
|
|
//! ```
|
|
|
|
use std::future::Future;
|
|
|
|
use kigi_test_support::*;
|
|
|
|
async fn with_local_set<F, Fut>(f: F)
|
|
where
|
|
F: FnOnce() -> Fut,
|
|
Fut: Future<Output = ()>,
|
|
{
|
|
tokio::task::LocalSet::new().run_until(f()).await;
|
|
}
|
|
|
|
/// Find `summary.json` for `session_id` under `<home>/.kigi/sessions/` and
|
|
/// parse it. The sessions tree is `<encoded-cwd>/<session-id>/summary.json`;
|
|
/// matching on the directory name avoids re-implementing the cwd encoding.
|
|
fn read_summary(home: &std::path::Path, session_id: &str) -> serde_json::Value {
|
|
let sessions_root = home.join(".kigi").join("sessions");
|
|
let cwd_dirs = std::fs::read_dir(&sessions_root)
|
|
.unwrap_or_else(|e| panic!("no sessions dir at {}: {e}", sessions_root.display()));
|
|
for cwd_dir in cwd_dirs.flatten() {
|
|
let candidate = cwd_dir.path().join(session_id).join("summary.json");
|
|
if candidate.is_file() {
|
|
let raw = std::fs::read_to_string(&candidate).expect("read summary.json");
|
|
return serde_json::from_str(&raw).expect("parse summary.json");
|
|
}
|
|
}
|
|
panic!(
|
|
"summary.json for session {session_id} not found under {}",
|
|
sessions_root.display()
|
|
);
|
|
}
|
|
|
|
/// A fresh session on a model with a configured reasoning effort must persist
|
|
/// that effort in `summary.json` without any model/effort switch.
|
|
// requires pre-built binary
|
|
#[tokio::test]
|
|
#[ignore]
|
|
async fn test_fresh_session_persists_reasoning_effort() {
|
|
with_local_set(|| async {
|
|
let server = MockInferenceServer::start()
|
|
.await
|
|
.expect("start mock server");
|
|
let workdir = git_workdir();
|
|
|
|
// Configure the mock catalog's model with an explicit effort via the
|
|
// user config override (the same path a remote settings catalog entry or
|
|
// `--effort` would populate).
|
|
let home = tempfile::TempDir::new().expect("create temp home");
|
|
let kigi_dir = home.path().join(".kigi");
|
|
std::fs::create_dir_all(&kigi_dir).expect("create .kigi dir");
|
|
std::fs::write(
|
|
kigi_dir.join("config.toml"),
|
|
r#"
|
|
[model.test-model]
|
|
supports_reasoning_effort = true
|
|
reasoning_effort = "high"
|
|
"#,
|
|
)
|
|
.expect("write config.toml");
|
|
|
|
let client = KigiStdioClient::spawn_with_home(&server, workdir.path(), home).await;
|
|
client.initialize_with_timeout().await;
|
|
let session_id = client.create_session_with_timeout(workdir.path()).await;
|
|
let result = client.prompt_with_timeout(&session_id, "say hello").await;
|
|
assert!(result.is_ok(), "prompt failed: {:?}", result.err());
|
|
|
|
let summary = read_summary(client.home_path(), &session_id.0);
|
|
assert_eq!(
|
|
summary.get("reasoning_effort").and_then(|v| v.as_str()),
|
|
Some("high"),
|
|
"fresh session must record its effort in summary.json; got: {summary}"
|
|
);
|
|
})
|
|
.await;
|
|
}
|
|
|
|
/// A fresh session on a model with no configured effort must not invent one:
|
|
/// `summary.json` omits the field (the model uses its server-side default).
|
|
// requires pre-built binary
|
|
#[tokio::test]
|
|
#[ignore]
|
|
async fn test_fresh_session_without_effort_omits_field() {
|
|
with_local_set(|| async {
|
|
let server = MockInferenceServer::start()
|
|
.await
|
|
.expect("start mock server");
|
|
let workdir = git_workdir();
|
|
let client = KigiStdioClient::spawn(&server, workdir.path()).await;
|
|
|
|
client.initialize_with_timeout().await;
|
|
let session_id = client.create_session_with_timeout(workdir.path()).await;
|
|
let result = client.prompt_with_timeout(&session_id, "say hello").await;
|
|
assert!(result.is_ok(), "prompt failed: {:?}", result.err());
|
|
|
|
let summary = read_summary(client.home_path(), &session_id.0);
|
|
assert_eq!(
|
|
summary.get("reasoning_effort"),
|
|
None,
|
|
"session without a configured effort must omit the field; got: {summary}"
|
|
);
|
|
})
|
|
.await;
|
|
}
|