Build checkpoint subtree paths via path.Join helper (no tree change) · Entire
Build checkpoint subtree paths via path.Join helper (no tree change)
4adc0c1·
Soph·2w ago·2 files·+117 added/-66 removed
A reviewer flagged fmt.Sprintf("%s%d/%s", basePath, idx, paths.MetadataFileName)
as looking like a missing slash — it's correct only because basePath carries a trailing "/". Replace the string-concat path construction with a
checkpointSubtreePath(base, segs...) helper over stdlib path.Join, so paths join
correctly without relying on that invariant.
- Helper uses the
pathpackage (NEVER path/filepath — git tree paths are always "/"; filepath.Join would emit "\" on Windows and corrupt tree keys). path.Join cleans separators, so base may be "" (ref root), "/ /" (v1, trailing slash), or a clean dir — all join identically. - Converted every full-path site and routed the directory-prefix intermediates through the helper as CLEAN dirs (sessionDir, taskDir), updating their consumers (writeSessionToSubdirectory, writeTranscript, writeCompactTranscript, replaceTranscript, replaceSkillEvents, copyMetadataDir, writeTaskCheckpoint*).
- The two prefix-scoping deletes that relied on the trailing slash now append it explicitly: HasPrefix(key, sessionDir+"/") (so "1" doesn't match sibling "10") and the transcript chunk cleanup. Leading-slash SessionFilePaths values become "/" + checkpointSubtreePath(...).
No behavior change: committed-tree assertions (checkpoint write/read, tripwire, update), integration (378), and both canary modes (git-branch 59/59, git-refs 58/59 +1 skip) are unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
973f8248e820View transcript
Changes
2
cmd/entire/cli/checkpoint
Mpersistent.go+84/-66
Asubtree_path_test.go+33
9 unmodified lines
10
11
12
13
14
15
16
122 unmodified lines
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
82 unmodified lines
239
240
241
229
242
243
244
245
32 unmodified lines
278
279
280
268
281
282
283
284
6 unmodified lines
291
292
293
281
294
295
296
297
36 unmodified lines
334
335
336
324
337
338
339
340
11 unmodified lines
352
353
354
342
355
356
357
358
11 unmodified lines
370
371
372
360
373
374
375
376
377
378
366
379
380
381
382
6 unmodified lines
389
390
391
379
392
393
394
395
3 unmodified lines
399
400
401
389
390
402
403
404
405
406
20 unmodified lines
427
428
429
417
418
430
431
432
433
434
435
436
437
438
425
439
440
441
442
3 unmodified lines
446
447
448
435
449
450
451
438
452
453
440
454
455
456
457
444
458
459
460
461
14 unmodified lines
476
477
478
465
479
480
481
482
3 unmodified lines
486
487
488
475
489
490
491
492
9 unmodified lines
502
503
504
491
505
506
507
508
19 unmodified lines
528
529
530
517
531
532
533
534
3 unmodified lines
538
539
540
527
528
541
542
543
544
545
14 unmodified lines
560
561
562
549
563
564
565
566
18 unmodified lines
585
586
587
574
588
589
590
591
9 unmodified lines
601
602
603
590
591
604
605
606
607
608
609
610
611
598
612
613
614
615
17 unmodified lines
633
634
635
622
636
637
638
639
11 unmodified lines
651
652
653
640
654
655
656
643
644
657
658
659
660
646
661
662
663
664
1 unmodified line
666
667
668
654
669
670
671
672
673
659
660
674
675
676
677
663
664
678
679
680
681
682
5 unmodified lines
688
689
690
676
677
691
692
693
694
695
696
681
697
698
699
700
37 unmodified lines
738
739
740
725
726
741
742
743
744
745
746
730
747
748
749
750
13 unmodified lines
764
765
766
750
767
768
769
770
37 unmodified lines
808
809
810
794
795
811
812
813
814
815
47 unmodified lines
863
864
865
849
866
867
868
869
22 unmodified lines
892
893
894
878
895
896
897
898
83 unmodified lines
982
983
984
968
985
986
987
988
38 unmodified lines
1027
1028
1029
1013
1030
1031
1032
1033
19 unmodified lines
1053
1054
1055
1039
1040
1056
1057
1058
1059
1060
1061
3 unmodified lines
1065
1066
1067
1050
1068
1069
1070
1071
20 unmodified lines
1092
1093
1094
1077
1095
1096
1097
1098
1099
1082
1100
1101
1102
1103
32 unmodified lines
1136
1137
1138
1121
1139
1140
1141
1142
617 unmodified lines
1760
1761
1762
1745
1746
1763
1764
1765
1766
1767
34 unmodified lines
1802
1803
1804
1787
1805
1806
1807
1808
10 unmodified lines
1819
1820
1821
1804
1822
1823
1824
1825
7 unmodified lines
1833
1834
1835
1818
1836
1837
1838
1839
21 unmodified lines
1861
1862
1863
1846
1864
1865
1866
1867
28 unmodified lines
1896
1897
1898
1881
1899
1900
1901
1902
142 unmodified lines
2045
2046
2047
2030
2048
2049
2050
2051
44 unmodified lines
2096
2097
2098
2081
2099
2100
2101
2102
9 unmodified lines
"io"
"log/slog"
"os"
"path"
"path/filepath"
"sort"
"strconv"
122 unmodified lines
return subtree, nil
}
// checkpointSubtreePath joins a checkpoint-relative git tree path from a base and
// trailing segments using path.Join. Git tree paths are always "/"-separated, so
// this uses the stdlib path package (never path/filepath, which would emit "\\" on
// Windows and corrupt tree keys). path.Join cleans separators, so base may be ""
// (per-checkpoint-ref root), a clean dir ("a3/b2.../0"), or a trailing-slash dir
// ("a3/b2..."): checkpointSubtreePath("", "0", "metadata.json") == "0/metadata.json"
// and checkpointSubtreePath("a3/b2.../", "0", "metadata.json") == "a3/b2.../0/metadata.json".
// Callers therefore need not maintain the trailing-slash invariant by hand.
func checkpointSubtreePath(base string, segs ...string) string {
return path.Join(append([]string{base}, segs...)...)
}
// flattenExisting flattens a checkpoint's current subtree into a path->entry map...
// (additional code snippets omitted for brevity)