supercode-harness 0.4.10

The optional native Supercode agent and tool harness
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
//! A12 acceptance tests (SPEC.md ยง6): `Session::to_jsonl_spliced` replays the
//! imported prefix verbatim to the origin format instead of resynthesizing
//! it, and `reduce::export_session_spliced` applies the same A11 leak guard
//! through that path.
//!
//! No test here reads an env var or is `#[ignore]`d โ€” every assertion runs on
//! a plain `cargo test`, matching `export_sidecar.rs`'s convention.

use std::path::{Path, PathBuf};

use supercode_harness::reduce::{export_session_spliced, REDUCTION_SENTINEL};
use supercode_harness::session::{Session, SessionFormat};
use supercode_harness::{ChatMessage, Role};

fn fixture(name: &str) -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("tests/fixtures")
        .join(name)
}

fn plain_message(role: Role, content: &str) -> ChatMessage {
    ChatMessage {
        role,
        content: Some(content.to_string()),
        content_parts: None,
        tool_calls: None,
        tool_call_id: None,
        name: None,
        metadata: Default::default(),
    }
}

fn two_synthetic_turns() -> Vec<ChatMessage> {
    vec![
        plain_message(Role::User, "Appended turn: what is 2+2?"),
        plain_message(Role::Assistant, "4."),
    ]
}

/// JSON-compare two lines after normalizing `key` out of both (setting it to
/// `Value::Null` on each side before comparing) โ€” the comparator the spec
/// prescribes for "equal modulo sessionId": every other field, including key
/// order via `serde_json::Value`'s structural equality, must match exactly.
fn json_eq_modulo_key(a: &str, b: &str, key: &str) -> bool {
    let mut va: serde_json::Value = serde_json::from_str(a).expect("a parses as JSON");
    let mut vb: serde_json::Value = serde_json::from_str(b).expect("b parses as JSON");
    if let Some(obj) = va.as_object_mut() {
        obj.insert(key.to_string(), serde_json::Value::Null);
    }
    if let Some(obj) = vb.as_object_mut() {
        obj.insert(key.to_string(), serde_json::Value::Null);
    }
    va == vb
}

fn non_empty_lines(s: &str) -> Vec<&str> {
    s.lines().filter(|l| !l.trim().is_empty()).collect()
}

// ---- 1. Claude Code origin: prefix verbatim, loader-skipped raw kept,
//         appended records chain and reload -------------------------------

#[test]
fn spliced_export_prefix_is_verbatim() {
    // A `file-history-snapshot` record has no canonical representation in
    // `Session::messages` (`session.rs` importer skips it โ€” it isn't
    // `user`/`assistant`/`attachment`/`system`) and the committed fixture
    // doesn't contain one, so build a small transcript inline that does,
    // rather than extending the shared fixture (not permitted by the spec).
    let inline = concat!(
        r#"{"type":"file-history-snapshot","messageId":"fhs-1","snapshot":{"trackedFileBackups":{}},"isSnapshotUpdate":false,"sessionId":"orig-session"}"#,
        "\n",
        r#"{"parentUuid":null,"type":"user","message":{"role":"user","content":"hello"},"uuid":"11111111-1111-4111-8111-111111111111","sessionId":"orig-session","cwd":"/work","timestamp":"2026-01-01T00:00:00.000Z"}"#,
        "\n",
        r#"{"parentUuid":"11111111-1111-4111-8111-111111111111","type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"hi there"}]},"uuid":"22222222-2222-4222-8222-222222222222","sessionId":"orig-session","cwd":"/work","timestamp":"2026-01-01T00:00:01.000Z"}"#,
        "\n",
    );
    let original = Session::from_claude_code_str(inline).unwrap();
    assert!(
        inline.contains("file-history-snapshot"),
        "sanity: the inline fixture actually carries a loader-skipped record"
    );

    let appended = two_synthetic_turns();
    let sidecar = original.to_native_jsonl_v2(&appended);
    let reconstructed = Session::from_sidecar_str(&sidecar).unwrap();

    let spliced = reconstructed
        .to_jsonl_spliced(SessionFormat::ClaudeCode, Some("new-session-id"))
        .unwrap();
    let spliced_lines = non_empty_lines(&spliced);
    let fixture_lines = non_empty_lines(inline);

    // (i) the first N emitted lines equal the fixture's N non-empty lines
    // modulo only `sessionId`.
    assert!(
        spliced_lines.len() >= fixture_lines.len(),
        "spliced output has fewer lines than the imported prefix:\n{spliced}"
    );
    for (i, (got, want)) in spliced_lines.iter().zip(fixture_lines.iter()).enumerate() {
        assert!(
            json_eq_modulo_key(got, want, "sessionId"),
            "prefix line {i} differs beyond sessionId:\n  got:  {got}\n  want: {want}"
        );
    }

    // (ii) the loader-skipped `file-history-snapshot` line is present in the
    // spliced output, but plain `to_claude_code_jsonl` (full synthesis)
    // never emits it โ€” the sentinel showing why A12 matters.
    assert!(
        spliced.contains("file-history-snapshot"),
        "spliced export must replay loader-skipped raw records verbatim:\n{spliced}"
    );
    let full_synth = reconstructed.to_jsonl(SessionFormat::ClaudeCode).unwrap();
    assert!(
        !full_synth.contains("file-history-snapshot"),
        "contrast check: full synthesis must NOT carry loader-skipped raw records:\n{full_synth}"
    );

    // Every emitted line's sessionId (where present) was rewritten to the
    // new id.
    for line in &spliced_lines {
        let v: serde_json::Value = serde_json::from_str(line).unwrap();
        if let Some(sid) = v.get("sessionId") {
            assert_eq!(
                sid, "new-session-id",
                "line did not get its sessionId rewritten: {line}"
            );
        }
    }

    // (iii) appended records parse, chain `parentUuid` to the last original
    // `uuid` (the assistant record's uuid โ€” the raw prefix's last uuid-
    // bearing line), and re-loading yields the original messages ++ the 2
    // new ones.
    let appended_lines = &spliced_lines[fixture_lines.len()..];
    assert_eq!(
        appended_lines.len(),
        2,
        "expected exactly 2 appended records"
    );
    let first_appended: serde_json::Value = serde_json::from_str(appended_lines[0]).unwrap();
    assert_eq!(
        first_appended["parentUuid"], "22222222-2222-4222-8222-222222222222",
        "first appended record must chain off the last original uuid, not a synth one"
    );

    let reloaded = Session::from_claude_code_str(&spliced).unwrap();
    let mut expected = original.messages.clone();
    expected.extend(appended.iter().cloned());
    assert_eq!(reloaded.messages.len(), expected.len());
    for (got, want) in reloaded.messages.iter().zip(expected.iter()) {
        assert_eq!(got.role, want.role);
        assert_eq!(got.content, want.content);
    }
}

#[test]
fn spliced_claude_continuation_preserves_native_timestamp_uuid_and_model() {
    let inline = concat!(
        r#"{"parentUuid":null,"type":"user","message":{"role":"user","content":"hello"},"uuid":"11111111-1111-4111-8111-111111111111","sessionId":"orig-session","cwd":"/work","timestamp":"2026-07-19T20:00:00.000Z"}"#,
        "\n",
        r#"{"parentUuid":"11111111-1111-4111-8111-111111111111","type":"assistant","message":{"role":"assistant","model":"claude-source-model","content":[{"type":"text","text":"hi"}]},"uuid":"22222222-2222-4222-8222-222222222222","sessionId":"orig-session","cwd":"/work","timestamp":"2026-07-19T20:00:01.000Z"}"#,
        "\n",
    );
    let original = Session::from_claude_code_str(inline).unwrap();
    assert_eq!(
        original.messages[1]
            .metadata
            .get("model")
            .map(String::as_str),
        Some("claude-source-model")
    );
    assert_eq!(
        original.messages[1]
            .metadata
            .get("claude_uuid")
            .map(String::as_str),
        Some("22222222-2222-4222-8222-222222222222")
    );
    let semantic = original.to_jsonl(SessionFormat::ClaudeCode).unwrap();
    let semantic_records: Vec<serde_json::Value> = semantic
        .lines()
        .map(|line| serde_json::from_str(line).unwrap())
        .collect();
    assert_eq!(
        semantic_records[0]["uuid"],
        "11111111-1111-4111-8111-111111111111"
    );
    assert_eq!(semantic_records[0]["parentUuid"], serde_json::Value::Null);
    assert_eq!(
        semantic_records[1]["uuid"],
        "22222222-2222-4222-8222-222222222222"
    );
    assert_eq!(
        semantic_records[1]["parentUuid"],
        "11111111-1111-4111-8111-111111111111"
    );
    assert_eq!(
        semantic_records[1]["message"]["model"],
        "claude-source-model"
    );

    let user = plain_message(Role::User, "continued through another provider");
    let mut assistant = plain_message(Role::Assistant, "continued answer");
    assistant
        .metadata
        .insert("model".to_string(), "z-ai/glm-5.2".to_string());
    let sidecar = original.to_native_jsonl_v2(&[user, assistant]);
    let reconstructed = Session::from_sidecar_str(&sidecar).unwrap();
    let spliced = reconstructed
        .to_jsonl_spliced(SessionFormat::ClaudeCode, None)
        .unwrap();
    let lines = non_empty_lines(&spliced);
    let tail: Vec<serde_json::Value> = lines[2..]
        .iter()
        .map(|line| serde_json::from_str(line).unwrap())
        .collect();
    assert_eq!(tail.len(), 2);

    let first_uuid = tail[0]["uuid"].as_str().unwrap();
    let second_uuid = tail[1]["uuid"].as_str().unwrap();
    for uuid in [first_uuid, second_uuid] {
        assert_eq!(uuid.len(), 36, "not UUID-shaped: {uuid}");
        assert_eq!(&uuid[14..15], "4", "not RFC4122 version 4: {uuid}");
        assert!(
            matches!(&uuid[19..20], "8" | "9" | "a" | "b"),
            "not RFC4122 variant: {uuid}"
        );
        assert!(!uuid.starts_with("00000000-0000-4000-8000-"));
    }
    assert_ne!(first_uuid, second_uuid);
    assert_eq!(
        tail[0]["parentUuid"],
        "22222222-2222-4222-8222-222222222222"
    );
    assert_eq!(tail[1]["parentUuid"], first_uuid);

    for record in &tail {
        let timestamp = record["timestamp"].as_str().unwrap();
        assert_ne!(timestamp, "2026-01-01T00:00:00.000Z");
        assert!(timestamp.ends_with('Z'));
    }
    assert_eq!(tail[1]["message"]["model"], "z-ai/glm-5.2");

    // Re-exporting the persisted sidecar is stable: identities and clocks do
    // not get minted again on each conversion.
    assert_eq!(
        spliced,
        reconstructed
            .to_jsonl_spliced(SessionFormat::ClaudeCode, None)
            .unwrap()
    );
}

// ---- 2. Codex origin: prefix envelopes verbatim modulo the id override,
//         appended response_items load back --------------------------------

#[test]
fn spliced_export_codex_prefix_is_verbatim() {
    let original = Session::from_codex(fixture("codex_session.jsonl")).unwrap();
    let appended = two_synthetic_turns();
    let sidecar = original.to_native_jsonl_v2(&appended);
    let reconstructed = Session::from_sidecar_str(&sidecar).unwrap();

    let spliced = reconstructed
        .to_jsonl_spliced(SessionFormat::Codex, Some("new-codex-id"))
        .unwrap();
    let spliced_lines = non_empty_lines(&spliced);
    let fixture_text = std::fs::read_to_string(fixture("codex_session.jsonl")).unwrap();
    let fixture_lines = non_empty_lines(&fixture_text);

    assert!(spliced_lines.len() >= fixture_lines.len());
    for (i, (got, want)) in spliced_lines.iter().zip(fixture_lines.iter()).enumerate() {
        let got_v: serde_json::Value = serde_json::from_str(got).unwrap();
        if got_v.get("type").and_then(serde_json::Value::as_str) == Some("session_meta") {
            // Byte-equal modulo `session_meta.payload.id`.
            let want_v: serde_json::Value = serde_json::from_str(want).unwrap();
            assert_eq!(got_v["payload"]["id"], "new-codex-id");
            let mut got_norm = got_v.clone();
            let mut want_norm = want_v.clone();
            got_norm["payload"]["id"] = serde_json::Value::Null;
            want_norm["payload"]["id"] = serde_json::Value::Null;
            assert_eq!(
                got_norm, want_norm,
                "session_meta line {i} differs beyond id"
            );
        } else {
            // Every other prefix line is untouched โ€” literally byte-equal.
            assert_eq!(*got, *want, "non-session_meta prefix line {i} was altered");
        }
    }

    let appended_lines = &spliced_lines[fixture_lines.len()..];
    assert_eq!(
        appended_lines.len(),
        2,
        "expected exactly 2 appended response_items"
    );
    for line in appended_lines {
        let v: serde_json::Value = serde_json::from_str(line).unwrap();
        assert_eq!(v["type"], "response_item");
    }

    let reloaded = Session::from_codex_str(&spliced).unwrap();
    let mut expected = original.messages.clone();
    expected.extend(appended.iter().cloned());
    assert_eq!(reloaded.messages.len(), expected.len());
    for (got, want) in reloaded.messages.iter().zip(expected.iter()) {
        assert_eq!(got.role, want.role);
        assert_eq!(got.content, want.content);
    }
}

// ---- 3. Cross-format: byte-identical to full synthesis --------------------

#[test]
fn spliced_export_cross_format_matches_full_synthesis() {
    let original = Session::from_claude_code(fixture("claude_code_session.jsonl")).unwrap();
    let appended = two_synthetic_turns();
    let sidecar = original.to_native_jsonl_v2(&appended);
    let reconstructed = Session::from_sidecar_str(&sidecar).unwrap();

    // Origin is Claude Code; requesting Codex has no verbatim prefix to
    // replay by definition, so splicing must fall back to full synthesis,
    // byte for byte.
    let spliced = reconstructed
        .to_jsonl_spliced(SessionFormat::Codex, None)
        .unwrap();
    let full = reconstructed.to_jsonl(SessionFormat::Codex).unwrap();
    assert_eq!(spliced, full);

    let codex_original = Session::from_codex(fixture("codex_session.jsonl")).unwrap();
    let codex_sidecar = codex_original.to_native_jsonl_v2(&appended);
    let codex_reconstructed = Session::from_sidecar_str(&codex_sidecar).unwrap();
    let spliced2 = codex_reconstructed
        .to_jsonl_spliced(SessionFormat::ClaudeCode, None)
        .unwrap();
    let full2 = codex_reconstructed
        .to_jsonl(SessionFormat::ClaudeCode)
        .unwrap();
    assert_eq!(spliced2, full2);
}

#[test]
fn spliced_cross_format_export_honors_requested_session_id() {
    let source = Session::from_grok(fixture("grok_session/chat_history.jsonl")).unwrap();
    let sidecar = source.to_native_jsonl_v2(&two_synthetic_turns());
    let reconstructed = Session::from_sidecar_str(&sidecar).unwrap();

    let exported = reconstructed
        .to_jsonl_spliced(
            SessionFormat::ClaudeCode,
            Some("11111111-2222-4333-8444-555555555555"),
        )
        .unwrap();
    let values = exported
        .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")
    }));
}

// ---- 4. export_session_spliced: grammar-aware leak guard -------------------

#[test]
fn export_session_spliced_allows_sentinel_mentions_but_rejects_exact_stubs() {
    let original = Session::from_claude_code(fixture("claude_code_session.jsonl")).unwrap();
    let genuine_but_sentinel_shaped = plain_message(
        Role::User,
        "please literally output [sc-reduced tool-output r0001-aaaa: not a real reduction]",
    );
    let sidecar = original.to_native_jsonl_v2(&[genuine_but_sentinel_shaped]);

    for format in [SessionFormat::ClaudeCode, SessionFormat::Codex] {
        let exported = export_session_spliced(&sidecar, format, None)
            .unwrap_or_else(|e| panic!("sentinel mention must remain exportable: {e}"));
        assert!(exported.contains("please literally output [sc-reduced"));
    }

    let exact_stub = plain_message(
        Role::Tool,
        "[sc-reduced tool-output r0001-aaaa: full output in session sidecar]",
    );
    let poisoned = original.to_native_jsonl_v2(&[exact_stub]);
    for format in [SessionFormat::ClaudeCode, SessionFormat::Codex] {
        let result = export_session_spliced(&poisoned, format, None);
        assert!(result.is_err(), "an exact reduction stub must fail closed");
        let msg = result.unwrap_err().to_string();
        assert!(
            msg.contains(REDUCTION_SENTINEL),
            "error message should name the sentinel it found: {msg:?}"
        );
    }

    let diffed_stub = plain_message(
        Role::Tool,
        "[sc-reduced file-read-diffed r0004-dddd: original file read in session sidecar]\n--- old\n+++ new",
    );
    let poisoned_diffed = original.to_native_jsonl_v2(&[diffed_stub]);
    assert!(
        export_session_spliced(&poisoned_diffed, SessionFormat::Codex, None).is_err(),
        "a reduction stub before a retained file diff must fail closed"
    );
}