use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use supercode::reduce::ReductionLog;
use supercode::session::Session;
use supercode::store::SessionStore;
use supercode::ChatMessage;
fn bin() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_supercode"))
}
fn fixture(name: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../harness/tests/fixtures")
.join(name)
}
fn fresh_home(tag: &str) -> PathBuf {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let dir = std::env::temp_dir().join(format!(
"supercode-convert-surface-{tag}-{}-{nanos}",
std::process::id()
));
std::fs::create_dir_all(&dir).unwrap();
dir
}
fn run(home: &Path, extra: &[&str]) -> Output {
let mut args = vec!["--api-key", "x", "--base-url", "http://127.0.0.1:1"];
args.extend_from_slice(extra);
Command::new(bin())
.env("SUPERCODE_HOME", home)
.env_remove("OPENROUTER_API_KEY")
.env_remove("OPENAI_API_KEY")
.env_remove("ANTHROPIC_API_KEY")
.args(&args)
.stdin(std::process::Stdio::null())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.output()
.expect("failed to spawn the supercode binary")
}
fn stderr(out: &Output) -> String {
String::from_utf8_lossy(&out.stderr).into_owned()
}
#[test]
fn convert_to_accepts_every_format() {
let home = fresh_home("to-oc-pi");
let f = fixture("claude_code_session.jsonl");
for target in [
"claude-code",
"codex",
"gemini",
"goose",
"grok",
"opencode",
"pi",
] {
let out_path = home.join(format!("out-{target}.jsonl"));
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
target,
"-o",
out_path.to_str().unwrap(),
],
);
assert!(
out.status.success(),
"convert --to {target} must be accepted by clap and succeed: {}",
stderr(&out)
);
assert!(
!stderr(&out).contains("invalid value"),
"clap must not reject `{target}`: {}",
stderr(&out)
);
}
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn audit_format_accepts_every_format() {
let home = fresh_home("audit-oc-pi");
let empty_dir = home.join("corpus");
std::fs::create_dir_all(&empty_dir).unwrap();
for format in [
"claude-code",
"codex",
"gemini",
"goose",
"grok",
"opencode",
"pi",
] {
let out = run(
&home,
&[
"audit",
empty_dir.to_str().unwrap(),
"--format",
format,
"--json",
],
);
assert!(
out.status.success(),
"audit --format {format} must be accepted by clap and succeed: {}",
stderr(&out)
);
assert!(
!stderr(&out).contains("invalid value"),
"clap must not reject `{format}`: {}",
stderr(&out)
);
}
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn diagonal_claude_to_claude_is_byte_identical() {
let home = fresh_home("diag-cc");
let f = fixture("claude_code_session.jsonl");
let out_path = home.join("roundtrip.jsonl");
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
assert!(
stderr(&out).contains("byte-identical"),
"success line must call out byte-identity: {}",
stderr(&out)
);
let original = std::fs::read(&f).unwrap();
let converted = std::fs::read(&out_path).unwrap();
assert_eq!(
original, converted,
"diagonal convert must reproduce the source file byte-for-byte"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn diagonal_codex_to_codex_is_byte_identical() {
let home = fresh_home("diag-codex");
let f = fixture("codex_session.jsonl");
let out_path = home.join("roundtrip.jsonl");
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"codex",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
let original = std::fs::read(&f).unwrap();
let converted = std::fs::read(&out_path).unwrap();
assert_eq!(
original, converted,
"diagonal convert must reproduce the source file byte-for-byte"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn diagonal_with_session_id_override_is_not_verbatim() {
let home = fresh_home("diag-sid");
let f = fixture("claude_code_session.jsonl");
let out_path = home.join("roundtrip.jsonl");
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"claude-code",
"--session-id",
"brand-new-id",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
assert!(
!stderr(&out).contains("byte-identical (verbatim)"),
"a session-id override must not claim byte-identity: {}",
stderr(&out)
);
let converted = std::fs::read_to_string(&out_path).unwrap();
assert!(converted.contains("brand-new-id"));
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn cross_format_convert_loads_back_via_target_loader() {
let home = fresh_home("cross-fmt");
let f = fixture("claude_code_session.jsonl");
let out_path = home.join("as-codex.jsonl");
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"codex",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
let err = stderr(&out);
assert!(
!err.contains("byte-identical"),
"cross-format export must not claim byte-identity: {err}"
);
assert!(
err.contains("not a parity guarantee"),
"cross-format export must not imply lossless parity: {err}"
);
let written = std::fs::read_to_string(&out_path).unwrap();
let reloaded = Session::from_codex_str(&written);
assert!(
reloaded.is_ok(),
"cross-format export must load back via from_codex_str: {reloaded:?}"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn zero_message_nonempty_input_warns_but_still_converts() {
let home = fresh_home("zero-msg");
let bad = home.join("unrecognized.jsonl");
std::fs::write(&bad, "{\"unrecognized\":true}\n{\"also_unrecognized\":1}\n").unwrap();
let out_path = home.join("out.jsonl");
let out = run(
&home,
&[
"convert",
bad.to_str().unwrap(),
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(
out.status.success(),
"a non-erroring zero-message load (no PARSE errors, just no recognized \
record shapes) must still convert (exit code unchanged): {}",
stderr(&out)
);
let err = stderr(&out);
assert!(
err.to_lowercase().contains("warning") && err.contains("zero messages"),
"must warn loudly about a non-empty input parsing to zero messages: {err}"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn all_malformed_json_fails_before_creating_or_truncating_output() {
let home = fresh_home("malformed-json");
let bad = home.join("corrupt.jsonl");
std::fs::write(&bad, "not json at all\nstill not json\n").unwrap();
let out_path = home.join("out.jsonl");
let sentinel = b"preexisting output must survive\n";
std::fs::write(&out_path, sentinel).unwrap();
let out = run(
&home,
&[
"convert",
bad.to_str().unwrap(),
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(
!out.status.success(),
"malformed JSON lines must fail loudly (nonzero exit), not silently \
succeed on a partially/never-loaded session: {}",
stderr(&out)
);
let err = stderr(&out);
assert!(
err.contains("2 of 2 line(s) failed to parse"),
"diagnostic must name the exact dropped-line count: {err}"
);
assert!(
err.contains("no output was written") && !err.contains("PARTIAL") && !err.contains('✓'),
"an all-invalid conversion has no recoverable partial payload: {err}"
);
assert_eq!(
std::fs::read(&out_path).unwrap(),
sentinel,
"all-invalid input must fail before truncating an existing --out file"
);
let stdout_out = run(
&home,
&["convert", bad.to_str().unwrap(), "--to", "claude-code"],
);
assert!(!stdout_out.status.success(), "{}", stderr(&stdout_out));
assert!(
stdout_out.stdout.is_empty(),
"all-invalid input must not emit a plausible empty session to stdout"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn one_bad_line_among_many_good_ones_still_converts_the_good_ones() {
let home = fresh_home("one-bad-line");
let f = home.join("mostly_good.jsonl");
let good_user = r#"{"type":"user","uuid":"u1","parentUuid":null,"sessionId":"s","message":{"role":"user","content":"hi"}}"#;
let good_asst = r#"{"type":"assistant","uuid":"a1","parentUuid":"u1","sessionId":"s","message":{"role":"assistant","content":[{"type":"text","text":"hello"}]}}"#;
std::fs::write(&f, format!("{good_user}\nnot valid json\n{good_asst}\n")).unwrap();
let out_path = home.join("out.jsonl");
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(
!out.status.success(),
"the one bad line must still fail loudly: {}",
stderr(&out)
);
assert!(
err_contains_dropped_count(&out, 1, 3),
"diagnostic must name the exact dropped-line count (1 of 3): {}",
stderr(&out)
);
assert!(
stderr(&out).contains("PARTIAL") && !stderr(&out).contains('✓'),
"the partial conversion banner must not claim success: {}",
stderr(&out)
);
let converted = std::fs::read_to_string(&out_path).unwrap();
assert!(converted.contains("\"content\":\"hi\""));
assert!(converted.contains("\"text\":\"hello\""));
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn parse_loss_refuses_before_overwriting_input_or_hard_link_alias() {
let home = fresh_home("parse-loss-alias");
let good_user = r#"{"type":"user","uuid":"u1","parentUuid":null,"sessionId":"s","message":{"role":"user","content":"hi"}}"#;
let good_asst = r#"{"type":"assistant","uuid":"a1","parentUuid":"u1","sessionId":"s","message":{"role":"assistant","content":[{"type":"text","text":"hello"}]}}"#;
let original = format!("{good_user}\nnot valid json\n{good_asst}\n").into_bytes();
for alias_kind in ["direct", "hard-link"] {
let input = home.join(format!("{alias_kind}-input.jsonl"));
std::fs::write(&input, &original).unwrap();
let out_path = if alias_kind == "direct" {
input.clone()
} else {
let alias = home.join("hard-link-output.jsonl");
std::fs::hard_link(&input, &alias).unwrap();
alias
};
let out = run(
&home,
&[
"convert",
input.to_str().unwrap(),
"--to",
"pi",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(!out.status.success(), "{alias_kind}: {}", stderr(&out));
let err = stderr(&out);
assert!(
err.contains("identifies the input file") && err.contains("no output was written"),
"{alias_kind}: diagnostic must explain the non-mutation: {err}"
);
assert_eq!(
std::fs::read(&input).unwrap(),
original,
"{alias_kind}: damaged input must remain byte-identical"
);
assert_eq!(
std::fs::read(&out_path).unwrap(),
original,
"{alias_kind}: output alias must remain byte-identical"
);
}
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn parse_loss_refuses_before_overwriting_attached_subagent_source() {
let home = fresh_home("parse-loss-subagent-alias");
let main_path = home.join("claude_code_session.jsonl");
let main = std::fs::read(fixture("claude_code_session.jsonl")).unwrap();
std::fs::write(&main_path, &main).unwrap();
let subagents_dir = home.join("claude_code_session").join("subagents");
std::fs::create_dir_all(&subagents_dir).unwrap();
let sub_path = subagents_dir.join("agent-deadbeef00000001.jsonl");
let sub = br#"{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"hi"}]},"agentId":"deadbeef00000001","sessionId":"sub"}
not valid json at all
"#;
for alias_kind in ["direct", "hard-link"] {
std::fs::write(&sub_path, sub).unwrap();
let out_path = if alias_kind == "direct" {
sub_path.clone()
} else {
let alias = home.join("subagent-hard-link-output.jsonl");
std::fs::hard_link(&sub_path, &alias).unwrap();
alias
};
let out = run(
&home,
&[
"convert",
main_path.to_str().unwrap(),
"--to",
"pi",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(!out.status.success(), "{alias_kind}: {}", stderr(&out));
assert!(
stderr(&out).contains("identifies source transcript")
&& stderr(&out).contains("no output was written"),
"{alias_kind}: diagnostic must identify the protected child: {}",
stderr(&out)
);
assert_eq!(
std::fs::read(&sub_path).unwrap(),
sub,
"{alias_kind}: damaged child source must remain byte-identical"
);
assert_eq!(
std::fs::read(&out_path).unwrap(),
sub,
"{alias_kind}: output alias must remain byte-identical"
);
}
let distinct = home.join("distinct-partial.jsonl");
let out = run(
&home,
&[
"convert",
main_path.to_str().unwrap(),
"--to",
"pi",
"-o",
distinct.to_str().unwrap(),
],
);
assert!(!out.status.success(), "distinct: {}", stderr(&out));
assert!(
distinct.exists() && !std::fs::read(&distinct).unwrap().is_empty(),
"a distinct path must retain the useful partial-output contract"
);
assert_eq!(std::fs::read(&main_path).unwrap(), main);
assert_eq!(std::fs::read(&sub_path).unwrap(), sub);
std::fs::remove_dir_all(&home).ok();
}
fn err_contains_dropped_count(out: &Output, dropped: usize, total: usize) -> bool {
stderr(out).contains(&format!("{dropped} of {total} line(s) failed to parse"))
}
#[test]
fn empty_input_converts_cleanly_with_no_warning() {
let home = fresh_home("empty-input");
let empty = home.join("empty.jsonl");
std::fs::write(&empty, "").unwrap();
let out_path = home.join("out.jsonl");
let out = run(
&home,
&[
"convert",
empty.to_str().unwrap(),
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
let err = stderr(&out);
assert!(
!err.to_lowercase().contains("warning"),
"a legitimately empty (zero-byte) input must not warn: {err}"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn diagonal_opencode_envelope_to_opencode_is_byte_identical() {
let home = fresh_home("diag-oc-envelope");
let f = fixture("opencode_session.jsonl");
let out_path = home.join("roundtrip.jsonl");
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"opencode",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
assert!(
stderr(&out).contains("byte-identical"),
"opencode envelope diagonal must call out byte-identity: {}",
stderr(&out)
);
let original = std::fs::read(&f).unwrap();
let converted = std::fs::read(&out_path).unwrap();
assert_eq!(
original, converted,
"opencode envelope diagonal convert must reproduce the source file byte-for-byte"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn diagonal_opencode_export_doc_to_opencode_is_not_byte_identical() {
let home = fresh_home("diag-oc-exportdoc");
let f = fixture("opencode_export_doc.json");
let out_path = home.join("roundtrip.json");
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"opencode",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
let err = stderr(&out);
assert!(
!err.contains("byte-identical"),
"an export-document source must NOT claim byte-identity, in any phrasing (raw is \
re-synthesized, not the original document's bytes): {err}"
);
assert!(
err.contains("re-serialized") && err.contains("export document"),
"the summary must give the ACTUAL honest reason (re-serialized from an export \
document), not a generic non-parity caption: {err}"
);
let original = std::fs::read(&f).unwrap();
let converted = std::fs::read(&out_path).unwrap();
assert_ne!(
original, converted,
"sanity: the fixture's pretty-printed export doc must not already be identical to \
the writer's own re-serialization byte-for-byte"
);
let reloaded = Session::from_opencode(&out_path)
.expect("the re-serialized output must still be a valid, loadable opencode session");
assert!(
!reloaded.messages.is_empty(),
"the re-serialized export-doc output must not silently be empty"
);
std::fs::remove_dir_all(&home).ok();
}
fn seed_reduced_store_session(home: &Path, name: &str) {
let f = fixture("claude_code_session.jsonl");
let session = Session::from_claude_code(&f).unwrap();
seed_reduced_store_from_session(home, name, &session, &[]);
}
fn seed_reduced_store_from_session(
home: &Path,
name: &str,
session: &Session,
appended: &[ChatMessage],
) {
let sidecar_jsonl = session.to_native_jsonl_v2(appended);
let transcript_jsonl = session
.messages
.iter()
.map(|m| serde_json::to_string(m).unwrap())
.collect::<Vec<_>>()
.join("\n");
let store = SessionStore::open(home.join("sessions")).unwrap();
store.save(name, "demo", &transcript_jsonl).unwrap();
store.save_sidecar(name, &sidecar_jsonl).unwrap();
store
.save_reduction_log(name, &ReductionLog::default())
.unwrap();
}
#[test]
fn reduced_store_diagonal_reports_observed_byte_identity() {
let home = fresh_home("reduced-no-sid");
seed_reduced_store_session(&home, "reduced-demo");
let out_path = home.join("out.jsonl");
let out = run(
&home,
&[
"convert",
"reduced-demo",
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
let err = stderr(&out);
assert!(
!err.contains("session id rewritten"),
"no --session-id was given — the summary must not attribute non-parity to an id \
rewrite that never happened: {err}"
);
assert!(
err.contains("byte-identical (verbatim)"),
"the sidecar splice reproduced the origin bytes and must report that fact: {err}"
);
assert_eq!(
std::fs::read(&out_path).unwrap(),
std::fs::read(fixture("claude_code_session.jsonl")).unwrap(),
"sanity: the reported byte identity must be real"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn reduced_store_diagonal_with_session_id_reports_rewrite() {
let home = fresh_home("reduced-with-sid");
seed_reduced_store_session(&home, "reduced-demo");
let out_path = home.join("out.jsonl");
let out = run(
&home,
&[
"convert",
"reduced-demo",
"--to",
"claude-code",
"--session-id",
"brand-new-id",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
let err = stderr(&out);
assert!(
err.contains("session id rewritten"),
"the explicit override rewrote otherwise-verbatim source bytes: {err}"
);
assert!(
!err.contains("byte-identical (verbatim)"),
"rewritten output must not claim byte identity: {err}"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn reduced_store_session_id_without_patchable_id_does_not_claim_rewrite() {
let home = fresh_home("reduced-empty-with-sid");
let empty = Session::from_claude_code_str("").unwrap();
seed_reduced_store_from_session(&home, "reduced-empty", &empty, &[]);
let out_path = home.join("out.jsonl");
let out = run(
&home,
&[
"convert",
"reduced-empty",
"--to",
"claude-code",
"--session-id",
"brand-new-id",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
let err = stderr(&out);
assert!(
!err.contains("session id rewritten"),
"an override that changed no bytes must not be named as the cause: {err}"
);
assert!(std::fs::read(&out_path).unwrap().is_empty());
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn reduced_store_with_continued_tail_does_not_claim_whole_file_identity() {
let home = fresh_home("reduced-continued-tail");
let source = Session::from_claude_code(fixture("claude_code_session.jsonl")).unwrap();
let appended = [ChatMessage::user("continued after rescue")];
seed_reduced_store_from_session(&home, "reduced-tail", &source, &appended);
let out_path = home.join("out.jsonl");
let out = run(
&home,
&[
"convert",
"reduced-tail",
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
let err = stderr(&out);
assert!(
err.contains("no byte-identity guarantee"),
"a synthesized continued tail prevents a whole-file identity claim: {err}"
);
assert!(!err.contains("byte-identical (verbatim)"), "{err}");
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn saved_cross_format_convert_honors_session_id_and_absolute_cwd() {
let home = fresh_home("saved-cross-format-overrides");
let mut source = Session::from_grok(fixture("grok_session/chat_history.jsonl")).unwrap();
source.meta.cwd = None;
seed_reduced_store_from_session(
&home,
"grok-continuation",
&source,
&[ChatMessage::user("continued through supercode")],
);
let out_path = home.join("handoff.jsonl");
let cwd = home.join("absolute-workspace");
std::fs::create_dir_all(&cwd).unwrap();
let out = run(
&home,
&[
"--cwd",
cwd.to_str().unwrap(),
"convert",
"grok-continuation",
"--to",
"claude-code",
"--session-id",
"11111111-2222-4333-8444-555555555555",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
let values = std::fs::read_to_string(&out_path)
.unwrap()
.lines()
.map(|line| serde_json::from_str::<serde_json::Value>(line).unwrap())
.collect::<Vec<_>>();
assert!(!values.is_empty());
assert!(values.iter().all(|value| {
value.get("sessionId").and_then(serde_json::Value::as_str)
== Some("11111111-2222-4333-8444-555555555555")
&& value.get("cwd").and_then(serde_json::Value::as_str) == cwd.to_str()
}));
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn path_diagonal_convert_honors_explicit_cwd_instead_of_replaying_raw() {
let home = fresh_home("path-diagonal-cwd");
let source_path = fixture("claude_code_session.jsonl");
let out_path = home.join("rewritten.jsonl");
let cwd = home.join("absolute-workspace");
std::fs::create_dir_all(&cwd).unwrap();
let out = run(
&home,
&[
"--cwd",
cwd.to_str().unwrap(),
"convert",
source_path.to_str().unwrap(),
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
assert!(stderr(&out).contains("session metadata rewritten"));
let values = std::fs::read_to_string(&out_path)
.unwrap()
.lines()
.map(|line| serde_json::from_str::<serde_json::Value>(line).unwrap())
.collect::<Vec<_>>();
assert!(!values.is_empty());
assert!(values
.iter()
.all(|value| { value.get("cwd").and_then(serde_json::Value::as_str) == cwd.to_str() }));
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn diagnostic_baseline_sentinel_never_blocks_safe_overridden_export() {
let home = fresh_home("reduced-sentinel-id");
let fixture_text = std::fs::read_to_string(fixture("claude_code_session.jsonl")).unwrap();
let hostile_id = "[sc-reduced-hostile-id";
let source_text = fixture_text.replace("213bb148-51ea-453f-9206-f8b4b1168547", hostile_id);
assert!(source_text.contains(hostile_id));
let source = Session::from_claude_code_str(&source_text).unwrap();
seed_reduced_store_from_session(&home, "reduced-sentinel", &source, &[]);
let out_path = home.join("out.jsonl");
let out = run(
&home,
&[
"convert",
"reduced-sentinel",
"--to",
"claude-code",
"--session-id",
"clean-id",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(
out.status.success(),
"a diagnostic-only baseline must not reject the safe requested export: {}",
stderr(&out)
);
let written = std::fs::read_to_string(&out_path).unwrap();
assert!(!written.contains("[sc-reduced"), "{written}");
assert!(written.contains("clean-id"), "{written}");
assert!(stderr(&out).contains("session id rewritten"));
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn path_convert_with_session_id_still_says_session_id_rewritten() {
let home = fresh_home("path-with-sid");
let f = fixture("claude_code_session.jsonl");
let out_path = home.join("out.jsonl");
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"claude-code",
"--session-id",
"brand-new-id",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(out.status.success(), "{}", stderr(&out));
assert!(
stderr(&out).contains("session id rewritten"),
"a genuine Path + --session-id same-format convert must still attribute \
non-parity to the id rewrite: {}",
stderr(&out)
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
#[ignore = "requires local session corpus; set SUPERCODE_CORPUS=1"]
fn parity8_diagonal_claude_to_claude_is_byte_identical_over_real_corpus() {
if std::env::var("SUPERCODE_CORPUS").is_err() {
panic!(
"SUPERCODE_CORPUS not set — this corpus test asserts nothing without \
the maintainer's local session logs; set SUPERCODE_CORPUS=1 to run it."
);
}
let claude_home = std::env::var("HOME").expect("HOME must be set");
let corpus_dir = Path::new(&claude_home).join(".claude/projects");
let mut sessions: Vec<PathBuf> = Vec::new();
collect_jsonl_files(&corpus_dir, &mut sessions);
sessions.sort_by_key(|p| std::cmp::Reverse(std::fs::metadata(p).map(|m| m.len()).unwrap_or(0)));
let home = fresh_home("parity8-corpus");
let mut checked = 0usize;
let mut non_identical: Vec<String> = Vec::new();
for f in sessions.into_iter().take(15) {
let original = match std::fs::read(&f) {
Ok(b) if !b.is_empty() => b,
_ => continue,
};
let out_path = home.join(format!("rt-{checked}.jsonl"));
let out = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
if !out.status.success() {
non_identical.push(format!("{}: convert exited non-zero", f.display()));
continue;
}
checked += 1;
let converted = std::fs::read(&out_path).unwrap_or_default();
if converted != original {
non_identical.push(format!(
"{}: diagonal convert NOT byte-identical ({} vs {} bytes)",
f.display(),
original.len(),
converted.len()
));
}
}
eprintln!(
"PARITY-8 real-corpus diagonal check: {checked} sessions, {} non-identical",
non_identical.len()
);
assert!(
checked >= 10,
"need at least 10 real Claude sessions to prove PARITY-8 at scale \
(dev/03); only found {checked} under {}",
corpus_dir.display()
);
assert!(
non_identical.is_empty(),
"diagonal convert was not byte-identical on {} real session(s):\n{}",
non_identical.len(),
non_identical.join("\n")
);
std::fs::remove_dir_all(&home).ok();
}
fn collect_jsonl_files(dir: &Path, out: &mut Vec<PathBuf>) {
let Ok(entries) = std::fs::read_dir(dir) else {
return;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
collect_jsonl_files(&path, out);
} else if path.extension().and_then(|e| e.to_str()) == Some("jsonl") {
out.push(path);
}
}
}
#[test]
fn real_session_truncated_mid_line_fails_loudly_on_inspect_and_convert() {
let home = fresh_home("real-truncated");
let full = std::fs::read_to_string(fixture("claude_code_session.jsonl")).unwrap();
let last_line_start = full.trim_end().rfind('\n').map(|i| i + 1).unwrap_or(0);
let last_line_len = full.trim_end().len() - last_line_start;
let cut_at = last_line_start + last_line_len / 2;
assert!(
cut_at > last_line_start && cut_at < full.trim_end().len(),
"sanity: the cut must land strictly inside the last line"
);
let truncated = &full[..cut_at];
let f = home.join("truncated_real_session.jsonl");
std::fs::write(&f, truncated).unwrap();
let insp = run(&home, &["inspect", f.to_str().unwrap()]);
assert!(
!insp.status.success(),
"inspect on a mid-line-truncated real session must fail loudly: {}",
stderr(&insp)
);
assert!(
stderr(&insp).contains("1 of") && stderr(&insp).contains("line(s) failed to parse"),
"diagnostic must name exactly 1 dropped line: {}",
stderr(&insp)
);
let stdout = String::from_utf8_lossy(&insp.stdout);
assert!(
stdout.contains("messages"),
"the intact earlier turns must still be shown, not suppressed entirely: {stdout}"
);
let insp_json = run(&home, &["inspect", f.to_str().unwrap(), "--json"]);
assert!(
!insp_json.status.success(),
"inspect --json must not silently bless a partial session: {}",
stderr(&insp_json)
);
let partial: serde_json::Value =
serde_json::from_slice(&insp_json.stdout).expect("partial inspect output stays valid JSON");
assert!(
partial["session"]["message_count"].as_u64().unwrap_or(0) > 0,
"intact prefix remains available in the partial JSON: {partial}"
);
assert!(
stderr(&insp_json).contains("1 of")
&& stderr(&insp_json).contains("line(s) failed to parse"),
"JSON-mode diagnostic must name the dropped line: {}",
stderr(&insp_json)
);
let out_path = home.join("out.jsonl");
let conv = run(
&home,
&[
"convert",
f.to_str().unwrap(),
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(
!conv.status.success(),
"convert on a mid-line-truncated real session must fail loudly: {}",
stderr(&conv)
);
assert!(
out_path.exists() && !std::fs::read_to_string(&out_path).unwrap().is_empty(),
"the intact prefix must still be written, not discarded"
);
std::fs::remove_dir_all(&home).ok();
}
#[test]
fn malformed_subagent_transcript_fails_loudly_on_inspect_and_convert() {
let home = fresh_home("bad-subagent");
let main_text = std::fs::read_to_string(fixture("claude_code_session.jsonl")).unwrap();
let subagents_dir = home.join("claude_code_session").join("subagents");
std::fs::create_dir_all(&subagents_dir).unwrap();
let main_path = home.join("claude_code_session.jsonl");
std::fs::write(&main_path, &main_text).unwrap();
let sub_ok = r#"{"type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"hi"}]},"agentId":"deadbeef00000001","sessionId":"sub"}"#;
std::fs::write(
subagents_dir.join("agent-deadbeef00000001.jsonl"),
format!("{sub_ok}\nnot valid json at all\n"),
)
.unwrap();
let insp = run(&home, &["inspect", main_path.to_str().unwrap()]);
assert!(
!insp.status.success(),
"a malformed subagent transcript must fail loudly on inspect, not \
silently convert with exit 0: {}",
stderr(&insp)
);
assert!(
stderr(&insp).contains("line(s) failed to parse"),
"diagnostic must name the dropped subagent line: {}",
stderr(&insp)
);
let out_path = home.join("out.jsonl");
let conv = run(
&home,
&[
"convert",
main_path.to_str().unwrap(),
"--to",
"claude-code",
"-o",
out_path.to_str().unwrap(),
],
);
assert!(
!conv.status.success(),
"a malformed subagent transcript must fail loudly on convert too: {}",
stderr(&conv)
);
std::fs::remove_dir_all(&home).ok();
}