@@ -14,7 +14,9 @@ import (
1414
1515const worktreeReclassificationSampleLimit = 10
1616
17- var ErrWorktreeMappingSetChanged = errors .New ("worktree mapping set changed" )
17+ var ErrWorktreeMappingSetChanged = errors .New (
18+ "worktree reclassification preview changed" ,
19+ )
1820
1921type WorktreeReclassificationDraft struct {
2022 Machine string `json:"machine"`
@@ -49,10 +51,11 @@ type WorktreeReclassificationPreview struct {
4951}
5052
5153type worktreeReclassificationEvaluation struct {
52- matched int
53- updates []worktreeMappingSessionUpdate
54- projects map [string ]int
55- sessions []WorktreeReclassificationSessionSample
54+ matched int
55+ updates []worktreeMappingSessionUpdate
56+ projects map [string ]int
57+ sessions []WorktreeReclassificationSessionSample
58+ impactToken string
5659}
5760
5861func (db * DB ) PreviewWorktreeReclassification (
@@ -120,8 +123,18 @@ func (db *DB) ApplyWorktreeReclassification(
120123 if err != nil {
121124 return WorktreeProjectMapping {}, WorktreeReclassificationPreview {}, err
122125 }
123- currentToken := worktreeMappingSetToken (stored )
124126 collision := exactWorktreeMapping (stored , normalized .PathPrefix )
127+ effective := overlayWorktreeMapping (stored , normalized , collision )
128+ evaluation , err := evaluateWorktreeMappingsTx (
129+ ctx , tx , normalized .Machine , enabledWorktreeMappings (effective ),
130+ & normalized , "" ,
131+ )
132+ if err != nil {
133+ return WorktreeProjectMapping {}, WorktreeReclassificationPreview {}, err
134+ }
135+ currentToken := worktreeReclassificationToken (
136+ stored , normalized , collision , evaluation ,
137+ )
125138 if acceptedToken == "" || acceptedToken != currentToken ||
126139 ! sameOptionalMappingID (existingMappingID , mappingIDPointer (collision )) {
127140 return WorktreeProjectMapping {}, WorktreeReclassificationPreview {},
@@ -134,18 +147,6 @@ func (db *DB) ApplyWorktreeReclassification(
134147 if err != nil {
135148 return WorktreeProjectMapping {}, WorktreeReclassificationPreview {}, err
136149 }
137- active , err := loadActiveWorktreeMappingsTx (ctx , tx , normalized .Machine )
138- if err != nil {
139- return WorktreeProjectMapping {}, WorktreeReclassificationPreview {}, fmt .Errorf (
140- "loading active worktree mappings: %w" , err ,
141- )
142- }
143- evaluation , err := evaluateWorktreeMappingsTx (
144- ctx , tx , normalized .Machine , active , & mapping , "" ,
145- )
146- if err != nil {
147- return WorktreeProjectMapping {}, WorktreeReclassificationPreview {}, err
148- }
149150 affected := map [string ]struct {}{mapping .Project : {}}
150151 updated := 0
151152 for _ , update := range evaluation .updates {
@@ -171,7 +172,7 @@ func (db *DB) ApplyWorktreeReclassification(
171172 }
172173
173174 preview := worktreeReclassificationPreviewFromEvaluation (
174- worktreeMappingSetTokenWithReplacement ( stored , mapping ) ,
175+ acceptedToken ,
175176 mapping .Project , mappingIDPointer (& mapping ), evaluation ,
176177 )
177178 preview .UpdatedSessions = updated
@@ -210,7 +211,8 @@ func previewWorktreeReclassificationTx(
210211 return WorktreeReclassificationPreview {}, err
211212 }
212213 return worktreeReclassificationPreviewFromEvaluation (
213- worktreeMappingSetToken (stored ), draft .Project ,
214+ worktreeReclassificationToken (stored , draft , collision , evaluation ),
215+ draft .Project ,
214216 mappingIDPointer (collision ), evaluation ,
215217 ), nil
216218}
@@ -353,6 +355,8 @@ func evaluateWorktreeMappingsTx(
353355 },
354356 )
355357
358+ impactHash := sha256 .New ()
359+ writeWorktreeTokenFields (impactHash , "impact-v1" )
356360 evaluation := worktreeReclassificationEvaluation {projects : map [string ]int {}}
357361 for _ , row := range sessions {
358362 if sessionID != "" && row .id != sessionID {
@@ -375,6 +379,18 @@ func evaluateWorktreeMappingsTx(
375379 continue
376380 }
377381 evaluation .matched ++
382+ matchCwd := row .matchCwd
383+ if matchCwd == "" {
384+ matchCwd = row .cwd
385+ }
386+ nextProject := row .project
387+ if shouldUpdate {
388+ nextProject = update .nextProject
389+ }
390+ writeWorktreeTokenFields (
391+ impactHash , row .id , row .project , nextProject , row .cwd ,
392+ matchCwd , row .filePath ,
393+ )
378394 if ! shouldUpdate {
379395 continue
380396 }
@@ -386,6 +402,7 @@ func evaluateWorktreeMappingsTx(
386402 NextProject : update .nextProject , Cwd : update .cwd ,
387403 })
388404 }
405+ evaluation .impactToken = hex .EncodeToString (impactHash .Sum (nil ))
389406 return evaluation , nil
390407}
391408
@@ -428,22 +445,41 @@ func worktreeMappingSetToken(mappings []WorktreeProjectMapping) string {
428445 fields := []string {
429446 strconv .FormatInt (mapping .ID , 10 ), mapping .Machine ,
430447 normalizedMappingPath (mapping .PathPrefix ), mapping .Layout ,
431- mapping .Project , strconv .FormatBool (mapping .Enabled ), mapping .UpdatedAt ,
432- }
433- for _ , field := range fields {
434- _ , _ = fmt .Fprintf (hash , "%d:%s" , len (field ), field )
448+ mapping .Project , mapping .OriginalProject ,
449+ strconv .FormatBool (mapping .Enabled ), mapping .UpdatedAt ,
435450 }
451+ writeWorktreeTokenFields (hash , fields ... )
436452 }
437453 return hex .EncodeToString (hash .Sum (nil ))
438454}
439455
440- func worktreeMappingSetTokenWithReplacement (
456+ func worktreeReclassificationToken (
441457 stored []WorktreeProjectMapping ,
442- mapping WorktreeProjectMapping ,
458+ draft WorktreeProjectMapping ,
459+ collision * WorktreeProjectMapping ,
460+ evaluation worktreeReclassificationEvaluation ,
443461) string {
444- return worktreeMappingSetToken (overlayWorktreeMapping (
445- stored , mapping , exactWorktreeMapping (stored , mapping .PathPrefix ),
446- ))
462+ collisionID := ""
463+ if collision != nil {
464+ collisionID = strconv .FormatInt (collision .ID , 10 )
465+ }
466+ hash := sha256 .New ()
467+ writeWorktreeTokenFields (
468+ hash , "reclassification-v2" , worktreeMappingSetToken (stored ),
469+ draft .Machine , normalizedMappingPath (draft .PathPrefix ), draft .Layout ,
470+ draft .Project , draft .OriginalProject , strconv .FormatBool (draft .Enabled ),
471+ collisionID , evaluation .impactToken ,
472+ )
473+ return hex .EncodeToString (hash .Sum (nil ))
474+ }
475+
476+ func writeWorktreeTokenFields (
477+ hash interface { Write ([]byte ) (int , error ) },
478+ fields ... string ,
479+ ) {
480+ for _ , field := range fields {
481+ _ , _ = fmt .Fprintf (hash , "%d:%s" , len (field ), field )
482+ }
447483}
448484
449485func mappingIDPointer (mapping * WorktreeProjectMapping ) * int64 {
0 commit comments