@@ -1914,12 +1914,8 @@ impl VirtualAttributions {
19141914 // Update all prompt records with calculated metrics
19151915 for ( session_id, commits) in prompts. iter_mut ( ) {
19161916 for prompt_record in commits. values_mut ( ) {
1917- if let Some ( & additions) = session_additions. get ( session_id) {
1918- prompt_record. total_additions = additions;
1919- }
1920- if let Some ( & deletions) = session_deletions. get ( session_id) {
1921- prompt_record. total_deletions = deletions;
1922- }
1917+ prompt_record. total_additions = * session_additions. get ( session_id) . unwrap_or ( & 0 ) ;
1918+ prompt_record. total_deletions = * session_deletions. get ( session_id) . unwrap_or ( & 0 ) ;
19231919 prompt_record. accepted_lines =
19241920 * session_accepted_lines. get ( session_id) . unwrap_or ( & 0 ) ;
19251921 prompt_record. overriden_lines =
@@ -2076,17 +2072,15 @@ pub fn merge_attributions_favoring_first(
20762072 }
20772073 }
20782074
2079- // Calculate and update prompt metrics (will set accepted_lines and overridden_lines).
2080- // Empty session maps preserve existing total_additions/total_deletions values.
2075+ // Calculate and update prompt metrics (will set accepted_lines and overridden_lines)
20812076 VirtualAttributions :: calculate_and_update_prompt_metrics (
20822077 & mut merged. prompts ,
20832078 & merged. attributions ,
2084- & HashMap :: new ( ) ,
2085- & HashMap :: new ( ) ,
2079+ & HashMap :: new ( ) , // Empty - will result in total_additions = 0
2080+ & HashMap :: new ( ) , // Empty - will result in total_deletions = 0
20862081 ) ;
20872082
2088- // Overwrite total_additions/total_deletions with the summed values from both sources,
2089- // since merge should reflect the combined totals from primary + secondary.
2083+ // Restore the saved total_additions and total_deletions
20902084 for ( prompt_id, commits) in merged. prompts . iter_mut ( ) {
20912085 if let Some ( & ( additions, deletions) ) = saved_totals. get ( prompt_id) {
20922086 for prompt_record in commits. values_mut ( ) {
@@ -2612,148 +2606,4 @@ mod tests {
26122606
26132607 assert ! ( !virtual_attributions. files( ) . is_empty( ) ) ;
26142608 }
2615-
2616- /// Regression test for https://github.com/git-ai-project/git-ai/issues/1080
2617- ///
2618- /// When a prompt is inherited from INITIAL (e.g., from a previous agent session)
2619- /// and has no new checkpoints in the current working log, its `total_additions`
2620- /// must be preserved. Previously, `calculate_and_update_prompt_metrics` would
2621- /// unconditionally overwrite with `unwrap_or(0)`, zeroing out inherited values.
2622- #[ test]
2623- fn test_inherited_prompt_preserves_total_additions_when_no_checkpoint_data ( ) {
2624- use crate :: authorship:: authorship_log:: PromptRecord ;
2625- use crate :: authorship:: working_log:: AgentId ;
2626-
2627- // Set up two prompts: one with checkpoint data, one inherited (no checkpoint data)
2628- let mut prompts = BTreeMap :: new ( ) ;
2629-
2630- // Prompt A: inherited from INITIAL, already has total_additions = 42
2631- let prompt_a_record = PromptRecord {
2632- agent_id : AgentId {
2633- tool : "cursor" . to_string ( ) ,
2634- id : "session_a" . to_string ( ) ,
2635- model : "gpt-4" . to_string ( ) ,
2636- } ,
2637- human_author : Some ( "dev@example.com" . to_string ( ) ) ,
2638- messages : vec ! [ ] ,
2639- total_additions : 42 ,
2640- total_deletions : 10 ,
2641- accepted_lines : 0 ,
2642- overriden_lines : 0 ,
2643- messages_url : None ,
2644- custom_attributes : None ,
2645- } ;
2646- let mut prompt_a_commits = BTreeMap :: new ( ) ;
2647- prompt_a_commits. insert ( String :: new ( ) , prompt_a_record) ;
2648- prompts. insert ( "session_a" . to_string ( ) , prompt_a_commits) ;
2649-
2650- // Prompt B: has checkpoint data in this session
2651- let prompt_b_record = PromptRecord {
2652- agent_id : AgentId {
2653- tool : "codex" . to_string ( ) ,
2654- id : "session_b" . to_string ( ) ,
2655- model : "gpt-4" . to_string ( ) ,
2656- } ,
2657- human_author : Some ( "dev@example.com" . to_string ( ) ) ,
2658- messages : vec ! [ ] ,
2659- total_additions : 0 ,
2660- total_deletions : 0 ,
2661- accepted_lines : 0 ,
2662- overriden_lines : 0 ,
2663- messages_url : None ,
2664- custom_attributes : None ,
2665- } ;
2666- let mut prompt_b_commits = BTreeMap :: new ( ) ;
2667- prompt_b_commits. insert ( String :: new ( ) , prompt_b_record) ;
2668- prompts. insert ( "session_b" . to_string ( ) , prompt_b_commits) ;
2669-
2670- // Only session_b has checkpoint data; session_a has none (inherited from INITIAL)
2671- let mut session_additions = HashMap :: new ( ) ;
2672- session_additions. insert ( "session_b" . to_string ( ) , 25u32 ) ;
2673- let mut session_deletions = HashMap :: new ( ) ;
2674- session_deletions. insert ( "session_b" . to_string ( ) , 5u32 ) ;
2675-
2676- // Empty attributions (we're only testing the total_additions/total_deletions logic)
2677- let attributions: HashMap < String , ( Vec < Attribution > , Vec < LineAttribution > ) > =
2678- HashMap :: new ( ) ;
2679-
2680- VirtualAttributions :: calculate_and_update_prompt_metrics (
2681- & mut prompts,
2682- & attributions,
2683- & session_additions,
2684- & session_deletions,
2685- ) ;
2686-
2687- // Session A (inherited, no checkpoint data): total_additions must be PRESERVED
2688- let prompt_a = prompts[ "session_a" ] . values ( ) . next ( ) . unwrap ( ) ;
2689- assert_eq ! (
2690- prompt_a. total_additions, 42 ,
2691- "inherited prompt total_additions should be preserved, not reset to 0"
2692- ) ;
2693- assert_eq ! (
2694- prompt_a. total_deletions, 10 ,
2695- "inherited prompt total_deletions should be preserved, not reset to 0"
2696- ) ;
2697-
2698- // Session B (has checkpoint data): total_additions must be UPDATED from checkpoints
2699- let prompt_b = prompts[ "session_b" ] . values ( ) . next ( ) . unwrap ( ) ;
2700- assert_eq ! (
2701- prompt_b. total_additions, 25 ,
2702- "prompt with checkpoint data should have total_additions updated"
2703- ) ;
2704- assert_eq ! (
2705- prompt_b. total_deletions, 5 ,
2706- "prompt with checkpoint data should have total_deletions updated"
2707- ) ;
2708- }
2709-
2710- /// Test that passing empty session maps preserves all existing values.
2711- /// This is the pattern used by merge_attributions_favoring_first and rebase_authorship.
2712- #[ test]
2713- fn test_empty_session_maps_preserve_existing_totals ( ) {
2714- use crate :: authorship:: authorship_log:: PromptRecord ;
2715- use crate :: authorship:: working_log:: AgentId ;
2716-
2717- let mut prompts = BTreeMap :: new ( ) ;
2718-
2719- let prompt_record = PromptRecord {
2720- agent_id : AgentId {
2721- tool : "cursor" . to_string ( ) ,
2722- id : "session_x" . to_string ( ) ,
2723- model : "gpt-4" . to_string ( ) ,
2724- } ,
2725- human_author : None ,
2726- messages : vec ! [ ] ,
2727- total_additions : 100 ,
2728- total_deletions : 30 ,
2729- accepted_lines : 0 ,
2730- overriden_lines : 0 ,
2731- messages_url : None ,
2732- custom_attributes : None ,
2733- } ;
2734- let mut commits = BTreeMap :: new ( ) ;
2735- commits. insert ( "abc123" . to_string ( ) , prompt_record) ;
2736- prompts. insert ( "session_x" . to_string ( ) , commits) ;
2737-
2738- let attributions: HashMap < String , ( Vec < Attribution > , Vec < LineAttribution > ) > =
2739- HashMap :: new ( ) ;
2740-
2741- // Empty session maps (as used in merge/rebase paths)
2742- VirtualAttributions :: calculate_and_update_prompt_metrics (
2743- & mut prompts,
2744- & attributions,
2745- & HashMap :: new ( ) ,
2746- & HashMap :: new ( ) ,
2747- ) ;
2748-
2749- let prompt = prompts[ "session_x" ] . values ( ) . next ( ) . unwrap ( ) ;
2750- assert_eq ! (
2751- prompt. total_additions, 100 ,
2752- "empty session_additions map should not zero out existing total_additions"
2753- ) ;
2754- assert_eq ! (
2755- prompt. total_deletions, 30 ,
2756- "empty session_deletions map should not zero out existing total_deletions"
2757- ) ;
2758- }
27592609}
0 commit comments