pitboard-core 0.15.3

The engine behind Pitboard: switches Claude Code, Codex and Claude Desktop between accounts by parking and restoring their logins. Its API serves Pitboard's own front ends.
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
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
//! Every fact about Claude Code that Pitboard stands on, named and dated.
//!
//! The keychain item's name and how the slot is hashed from a directory, the five keys a
//! logout deletes, the write lock, the refresh lock and their constants, the one write that
//! skips the write lock, the 4032-byte ceiling on a `security` command, the thirty seconds a
//! session caches a credential for. Each was read out of the build its entry names.
//!
//! None of it transfers. The next provider's equivalents have to be read out of its own
//! build the same way, into a list of its own, dated on its own schedule.
//!
//! See [`crate::assumptions`] for what an entry means and how a probe reads one.

use crate::assumptions::OnSystem::{NotRead, Pending, Read};
use crate::assumptions::{Assumption, OnSystem, PerSystem};

/// The build every entry below was read from on macOS and Linux, unless it says otherwise.
pub(crate) const VERIFIED_AGAINST: &str = "2.1.284";

/// The build the facts read on Windows were read from: `@anthropic-ai/claude-code-win32-x64`
/// and `-win32-arm64` 2.1.289, both built from commit 736d26e, as that version's macOS and
/// Linux builds are. Read as bytes on a Mac on 2026-10-06, and never run.
pub(crate) const WINDOWS_VERIFIED_AGAINST: &str = "2.1.289";

/// The Linux build has no keychain backend, so a keychain fact read from it reports the
/// keychain gone, as it did on every build from 2.1.278 on.
const NO_KEYCHAIN_ON_LINUX: OnSystem =
    NotRead("Linux has no keychain, and Claude Code's Linux build has no keychain backend");

/// Nor has the Windows build. Both Windows builds of 2.1.289 keep code they share with the
/// macOS one: the fallback wrapper's `keychain_locked_skip_fallback` and
/// `primary_transient_skip_fallback`, and a device key store that names
/// `find-generic-password`. What they lack is the backend itself: none holds `exceeds
/// security -i stdin limit; using argv`, `show-keychain-info` or `[keychain] readAsync
/// failed`, which the macOS build holds twice each.
const NO_KEYCHAIN_ON_WINDOWS: OnSystem =
    NotRead("Windows has no keychain, and Claude Code's Windows build has no keychain backend");

/// A fact read on macOS and Linux from the build its entry names, and on Windows from
/// [`WINDOWS_VERIFIED_AGAINST`].
const fn everywhere(name: &'static str, build: &'static str) -> PerSystem {
    PerSystem {
        name,
        macos: Read(build),
        linux: Read(build),
        windows: Read(WINDOWS_VERIFIED_AGAINST),
    }
}

// Read on 12 October 2026 from 2.1.296's macOS and Linux builds, and measured on macOS.
const fn warm_up(name: &'static str) -> PerSystem {
    PerSystem {
        name,
        macos: Read("2.1.296"),
        linux: Read("2.1.296"),
        windows: NotRead(
            "Pitboard warms up no account on Windows, where Claude Code reads no token handed to \
             it: windows_token_handover",
        ),
    }
}

// Anthropic's, not the build's, so the same whatever system sends the request.
const fn of_the_service(name: &'static str) -> PerSystem {
    PerSystem {
        name,
        macos: Read("2.1.296"),
        linux: Read("2.1.296"),
        windows: Read("2.1.296"),
    }
}

/// A fact about what Anthropic answers and how Claude Code reads it, read on 16 October 2026
/// from 2.1.301's macOS, Linux and both Windows builds as bytes, whose code here is the same.
const fn answers(name: &'static str) -> PerSystem {
    PerSystem {
        name,
        macos: Read("2.1.301"),
        linux: Read("2.1.301"),
        windows: Read("2.1.301"),
    }
}

/// What each fact below is on each system. The Windows readings are a reading of the
/// Windows builds' code for each fact, and of every literal its entry probes for, which
/// both Windows builds hold. Where Claude Code does something else on Windows, that is a
/// fact of its own, read by the pull request named.
pub(crate) const PER_SYSTEM: &[PerSystem] = &[
    PerSystem {
        name: "credential_service_name",
        macos: Read(VERIFIED_AGAINST),
        linux: NO_KEYCHAIN_ON_LINUX,
        windows: NotRead(
            "the Windows build keeps no keychain item; the Credential Manager item it keeps the \
             login in under the same service is credman_target's",
        ),
    },
    PerSystem {
        name: "keychain_account_name",
        macos: Read(VERIFIED_AGAINST),
        linux: NO_KEYCHAIN_ON_LINUX,
        windows: NotRead(
            "the Windows build names its Credential Manager item's user with a literal, which \
             credman_account_name reads",
        ),
    },
    PerSystem {
        name: "live_chain_order",
        macos: Read(VERIFIED_AGAINST),
        linux: Read(VERIFIED_AGAINST),
        windows: NotRead(
            "which store Claude Code keeps the login in on Windows is windows_backend_choice's, \
             and how it moves the login into Credential Manager is credman_migration's",
        ),
    },
    PerSystem {
        name: "keychain_write_route",
        macos: Read(VERIFIED_AGAINST),
        linux: NO_KEYCHAIN_ON_LINUX,
        windows: NO_KEYCHAIN_ON_WINDOWS,
    },
    PerSystem {
        name: "keychain_absence_codes",
        macos: Read(VERIFIED_AGAINST),
        linux: NO_KEYCHAIN_ON_LINUX,
        windows: NO_KEYCHAIN_ON_WINDOWS,
    },
    // Read again in both Windows builds of 2.1.300. Under Bun 1.4.3, proper-lockfile 4.1.2 and
    // Pitboard's lock see each other's lock with this timing on both runner images (RF-C5,
    // run 38693947329).
    PerSystem {
        name: "write_lock",
        macos: Read(VERIFIED_AGAINST),
        linux: Read(VERIFIED_AGAINST),
        windows: Read("2.1.300"),
    },
    everywhere("logout_skips_the_lock", VERIFIED_AGAINST),
    everywhere("account_scoped_keys", VERIFIED_AGAINST),
    PerSystem {
        name: "credential_cache",
        macos: Read("2.1.278"),
        linux: Read("2.1.278"),
        windows: NotRead(
            "how long a session keeps a login read from Credential Manager is \
             windows_credman_cache's, and how soon it takes a new `.credentials.json` \
             windows_file_adoption's",
        ),
    },
    PerSystem {
        name: "config_file_location",
        macos: Read("2.1.301"),
        linux: Read("2.1.301"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "settings_byte_order_mark",
        macos: Read("2.1.302"),
        linux: Read("2.1.302"),
        windows: Read("2.1.302"),
    },
    everywhere("oauth_client", VERIFIED_AGAINST),
    PerSystem {
        name: "supervisor_daemon",
        macos: Read(VERIFIED_AGAINST),
        linux: Read(VERIFIED_AGAINST),
        windows: NotRead(
            "which process the lock names on Windows is windows_daemon_identity's, read from \
             the Windows builds",
        ),
    },
    PerSystem {
        name: "no_keyring_off_macos",
        macos: NotRead(
            "this is about the systems without a keychain, and macOS keeps the login in one",
        ),
        linux: Read(VERIFIED_AGAINST),
        windows: NotRead(
            "Claude Code's Credential Manager store on Windows calls `Bun.secrets`, which this \
             fact rules out, so read from the Windows build it reports a keyring arrived",
        ),
    },
    // Read again in both Windows builds of 2.1.300, where a line is read with readline and
    // trimmed, so one typed back with `\r\n` is one with `\n`.
    PerSystem {
        name: "sign_in_output",
        macos: Read("2.1.289"),
        linux: Read("2.1.289"),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "sign_in_takes_another_code",
        macos: Read("2.1.289"),
        linux: Read("2.1.289"),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "install_places",
        macos: Read("2.1.289"),
        linux: Read("2.1.289"),
        windows: NotRead(
            "the native installer, npm and winget put `claude.exe` elsewhere on Windows, which \
             windows_install_layout reads",
        ),
    },
    PerSystem {
        name: "plaintext_credential_mode",
        macos: Read(VERIFIED_AGAINST),
        linux: Read(VERIFIED_AGAINST),
        windows: NotRead(
            "a mode keeps nobody out of a file on Windows; how Claude Code writes \
             `.credentials.json` there is windows_plaintext_write's",
        ),
    },
    PerSystem {
        name: "locked_keychain_keeps_last_login",
        macos: Read("2.1.294"),
        linux: NO_KEYCHAIN_ON_LINUX,
        windows: NO_KEYCHAIN_ON_WINDOWS,
    },
    PerSystem {
        name: "locked_sign_in_writes_fallback",
        macos: Read("2.1.294"),
        linux: NO_KEYCHAIN_ON_LINUX,
        windows: NotRead(
            "Windows has no keychain; a session out of Credential Manager's reach writes the \
             file, credman_unreachable_writes_file's",
        ),
    },
    PerSystem {
        name: "fallback_outlives_keychain_writes",
        macos: Read("2.1.294"),
        linux: NO_KEYCHAIN_ON_LINUX,
        windows: NO_KEYCHAIN_ON_WINDOWS,
    },
    // Read again in both Windows builds of 2.1.301, where the file sits behind Credential
    // Manager wherever that store is on.
    PerSystem {
        name: "fallback_file_pins_session_login",
        macos: Read("2.1.294"),
        linux: NO_KEYCHAIN_ON_LINUX,
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "login_is_one_organisation",
        macos: Read("2.1.294"),
        linux: Read("2.1.294"),
        windows: Read("2.1.294"),
    },
    PerSystem {
        name: "config_may_name_no_organisation",
        macos: Read("2.1.294"),
        linux: Read("2.1.294"),
        windows: Read("2.1.294"),
    },
    PerSystem {
        name: "usage_cache_stamp_is_the_configs",
        macos: Read("2.1.294"),
        linux: Read("2.1.294"),
        windows: Read("2.1.294"),
    },
    PerSystem {
        name: "status_reads_the_config_usage_the_token",
        macos: Read("2.1.294"),
        linux: Read("2.1.294"),
        windows: Read("2.1.294"),
    },
    PerSystem {
        name: "config_identity_is_the_last_writers",
        macos: Read("2.1.294"),
        linux: Read("2.1.294"),
        windows: Read("2.1.294"),
    },
    PerSystem {
        name: "status_line_input_names_no_account",
        macos: Read("2.1.294"),
        linux: Read("2.1.294"),
        windows: Read("2.1.294"),
    },
    // Read again in both Windows builds of 2.1.300, and measured with this timing as the write
    // lock was (RF-C5r, run 38693947329).
    PerSystem {
        name: "refresh_lock",
        macos: Read("2.1.294"),
        linux: Read("2.1.294"),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "login_carries_its_plan",
        macos: Read("2.1.296"),
        linux: Read("2.1.296"),
        windows: Read("2.1.296"),
    },
    PerSystem {
        name: "windows_home_folder",
        macos: NotRead("macOS keeps the home in `HOME`, which config_file_location reads"),
        linux: NotRead("Linux keeps the home in `HOME`, which config_file_location reads"),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "windows_open_sharing",
        macos: NotRead("a rename lands over a file open on macOS whatever opened it"),
        linux: NotRead("a rename lands over a file open on Linux whatever opened it"),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "windows_install_layout",
        macos: NotRead("where macOS's installers put `claude` is install_places'"),
        linux: NotRead("where Linux's installers put `claude` is install_places'"),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "windows_version_resource",
        macos: NotRead("a macOS program carries no version resource"),
        linux: NotRead("a Linux program carries no version resource"),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "windows_managed_settings",
        macos: NotRead("macOS has no registry, and its managed folder is settings.rs's"),
        linux: NotRead("Linux has no registry, and its managed folder is settings.rs's"),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "windows_daemon_identity",
        macos: NotRead("off Windows the daemon records `procStart`, which Pitboard does not read"),
        linux: NotRead("off Windows the daemon records `procStart`, which Pitboard does not read"),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "windows_backend_choice",
        macos: NotRead("macOS's chain is live_chain_order's"),
        linux: NotRead("Linux's store is the file alone, which no_keyring_off_macos reads"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "windows_plaintext_strict_read",
        macos: NotRead(
            "off Windows no reader keeps another out of a file, and a refused read is no login",
        ),
        linux: NotRead(
            "off Windows no reader keeps another out of a file, and a refused read is no login",
        ),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "windows_plaintext_write",
        macos: NotRead("how macOS's file is written is plaintext_credential_mode's"),
        linux: NotRead("how Linux's file is written is plaintext_credential_mode's"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "windows_file_adoption",
        macos: NotRead(
            "on macOS the file sits behind the keychain, fallback_file_pins_session_login's",
        ),
        linux: NotRead("how soon a session takes a new login off Windows is credential_cache's"),
        windows: Read("2.1.302"),
    },
    PerSystem {
        name: "credman_account_name",
        macos: NotRead("macOS's account is keychain_account_name's"),
        linux: NotRead("Linux keeps the login in a file, which has no user name"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "credman_target",
        macos: NotRead("macOS's item is credential_service_name's"),
        linux: NotRead("Linux keeps the login in a file"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "credman_chunking",
        macos: NotRead("a keychain item takes a login of any size, keychain_write_route's"),
        linux: NotRead("Linux keeps the login in a file"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "credman_probe",
        macos: NotRead("macOS's chain is live_chain_order's"),
        linux: NotRead("Linux keeps the login in a file"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "credman_migration",
        macos: NotRead("macOS's chain is live_chain_order's"),
        linux: NotRead("Linux keeps the login in a file"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "credman_move_lost_a_piece",
        macos: NotRead("Linux and macOS keep no login in Credential Manager"),
        linux: NotRead("Linux and macOS keep no login in Credential Manager"),
        windows: Read("2.1.302"),
    },
    PerSystem {
        name: "credman_unreachable_writes_file",
        macos: NotRead("macOS's counterpart is locked_sign_in_writes_fallback"),
        linux: NotRead("Linux keeps the login in a file"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "credman_attributes",
        macos: NotRead("macOS's item is written by `security`, keychain_write_route's"),
        linux: NotRead("Linux keeps the login in a file"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "windows_credman_cache",
        macos: NotRead("how soon a session takes a new login on macOS is credential_cache's"),
        linux: NotRead("how soon a session takes a new login on Linux is credential_cache's"),
        windows: Pending {
            by: &["W27"],
            reads: "how soon a running session takes a login a switch wrote to Credential \
                    Manager, session step 7, which the session of 2026-10-18 did not reach: its \
                    switches wrote `.credentials.json` past a settings file Pitboard read no \
                    `env` from (settings_byte_order_mark), then refused over the pieces Claude \
                    Code had lost a login in (credman_move_lost_a_piece)",
        },
    },
    PerSystem {
        name: "windows_browser_open",
        macos: NotRead(
            "off Windows a cancel stops the tool alone, so how it opens a browser decides nothing",
        ),
        linux: NotRead(
            "off Windows a cancel stops the tool alone, so how it opens a browser decides nothing",
        ),
        windows: Read("2.1.300"),
    },
    PerSystem {
        name: "windows_token_handover",
        macos: NotRead("macOS and Linux hand a descriptor as headless_token_descriptor says"),
        linux: NotRead("macOS and Linux hand a descriptor as headless_token_descriptor says"),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "windows_statusline_shell",
        macos: NotRead(
            "off Windows a status line runs in the person's shell, which finds pitboard",
        ),
        linux: NotRead(
            "off Windows a status line runs in the person's shell, which finds pitboard",
        ),
        windows: Read("2.1.301"),
    },
    PerSystem {
        name: "windows_statusline_powershell",
        macos: NotRead("only Windows runs a status line in PowerShell"),
        linux: NotRead("only Windows runs a status line in PowerShell"),
        windows: Read("2.1.302"),
    },
    PerSystem {
        name: "proxy_from_variables",
        macos: Read("2.1.301"),
        linux: Read("2.1.301"),
        windows: Read("2.1.301"),
    },
    warm_up("headless_token_descriptor"),
    warm_up("headless_print_mode"),
    warm_up("headless_result"),
    of_the_service("request_starts_the_five_hour_period"),
    of_the_service("weekly_period_is_a_fixed_cycle"),
    of_the_service("used_up_limit_refuses_every_request"),
    answers("refusals_are_final"),
    answers("account_refusals"),
    answers("token_refusals"),
    answers("validate_names_the_login"),
    answers("token_answer_names_the_login"),
];

pub(crate) const ASSUMPTIONS: &[Assumption] = &[
    Assumption {
        name: "credential_service_name",
        fact: "the login lives in a keychain item named `Claude Code${OAUTH_FILE_SUFFIX}-credentials`, \
               with `-<first 8 hex of sha256 of the NFC-normalised storage directory>` appended for \
               any directory but the default",
        read_from: "the secure storage module's service name and slot derivation",
        verified_against: VERIFIED_AGAINST,
        depends: "slot.rs, and every read and write of the live credential",
        probe: &["-credentials", "OAUTH_FILE_SUFFIX"],
        absent: &[],
    },
    Assumption {
        name: "keychain_account_name",
        fact: "the keychain account is $USER when it matches /^[a-zA-Z0-9._-]+$/, and \
               `claude-code-user` when it does not",
        read_from: "the secure storage module's account name",
        verified_against: VERIFIED_AGAINST,
        depends: "slot::account_name",
        probe: &["claude-code-user"],
        absent: &[],
    },
    Assumption {
        name: "live_chain_order",
        fact: "on macOS the login is read from the keychain first and a plaintext \
               `.credentials.json` second, and a write moves it to the file only when the \
               keychain write fails for good; on Linux the file is the only store. The \
               successor backend replaces the fallback half and only for a caller that hands a \
               backend in, which an ordinary `claude` does not",
        read_from: "the keychain-with-plaintext-fallback store and `getSecureStorage`",
        verified_against: VERIFIED_AGAINST,
        depends: "store::resolve and everything that reads or writes through it",
        probe: &[".credentials.json", "-with-", "-fallback"],
        absent: &[],
    },
    Assumption {
        name: "keychain_write_route",
        fact: "a write gives `security -i` the line `add-generic-password -U -a \"<account>\" \
               -s \"<service>\" -X \"<hex>\"` on stdin while that line is at most 4032 bytes, \
               and the same command on the argument line above that; it never refuses or \
               splits a login. Each call has a 2 second timeout. A failed write is transient, \
               and skips the plaintext fallback, when it timed out or, from 2.1.281, when it \
               exited 36 after this process had seen the item; any other failure moves the \
               login to `.credentials.json`",
        read_from: "the keychain backend's update path and the fallback wrapper around it",
        verified_against: VERIFIED_AGAINST,
        depends: "host::macos::keychain::MAX_COMMAND_BYTES and the write path",
        probe: &[
            "exceeds security -i stdin limit; using argv",
            "primary_transient_skip_fallback",
            "keychain_locked_skip_fallback",
        ],
        absent: &[],
    },
    Assumption {
        name: "keychain_absence_codes",
        fact: "`find-generic-password -a <account> -w -s <service>`, with a 2 second timeout: \
               exit 0 with output is the login; exit 0 with nothing, exit 44, or output that is \
               not JSON is absent. Exit 36, a locked keychain, is absent to an ordinary read, a \
               failed read to a write once this process has seen the item \
               (`failureIfTransient`, from 2.1.281), and a failed read outright only when a \
               caller asks for that. Any other exit is a failed read. `show-keychain-info` \
               exiting 36 only adds an unlock hint. Pitboard reads 36 as a locked keychain, \
               never as absent, the strict end of that",
        read_from: "the keychain backend's read path",
        verified_against: VERIFIED_AGAINST,
        depends: "host::macos::keychain::classify",
        probe: &[
            "failureIfTransient",
            "[keychain] readAsync failed; not caching a null",
            "show-keychain-info",
        ],
        absent: &[],
    },
    Assumption {
        name: "write_lock",
        fact: "every credential write takes proper-lockfile's directory lock at \
               `<storage dir>/.storage-write`, stale 15000ms, ten retries, 100ms to 1000ms of \
               backoff, and re-reads the credential inside the lock before changing it. A \
               re-read that fails abandons the write (`read_failed_skip_write`); from 2.1.281 a \
               locked keychain counts as a failed re-read once this process has seen the item, \
               where before it read as empty",
        read_from: "the secure storage module's write wrapper",
        verified_against: VERIFIED_AGAINST,
        depends: "lock.rs, the whole switch, and `pitboard stow`, which takes it once before it \
                  renews a login left in `.credentials.json`, and holds it while it writes that \
                  login back renewed and while it reads the file and the login stored a last time \
                  and deletes the file",
        probe: &[
            ".storage-write",
            "[secureStorage] write lock compromised: ",
            "read_failed_skip_write",
            "failureIfTransient",
        ],
        absent: &[],
    },
    Assumption {
        name: "logout_skips_the_lock",
        fact: "`/logout` retries for its own 7.5 seconds and then deletes the credential with \
               no lock held at all",
        read_from: "the secure storage module's already-locked escape hatch",
        verified_against: VERIFIED_AGAINST,
        depends: "the slot re-read in switch, which exists for this",
        probe: &["secureStorage.READ_FAILED"],
        absent: &[],
    },
    Assumption {
        name: "refresh_lock",
        fact: "a renewal of the login takes proper-lockfile's directory lock \
               `<storage dir>/.oauth_refresh.lock`, stale 60000ms and touched every 5000ms, then \
               the legacy `<storage dir, links resolved>.lock` the same way, letting go of the \
               first where the second is held and going without the second where it cannot be \
               made. Under them it reads the login again, sends its refresh token, and saves the \
               answer through the write lock only where the login stored still holds the token \
               it sent, writing nothing otherwise. So the write lock does not keep a renewal \
               from spending a refresh token: this lock does. A holder is taken over before \
               the lock is stale only where its owner record, \
               `<storage dir>/.oauth_refresh.lock.owner`, proves it gone",
        read_from: "the refresh lock's options and the function taking both locks, the token \
                    refresh and the scope expansion, which take it before they read the login \
                    again and send the refresh token, the refresh's compare-and-set save through \
                    the storage write wrapper, and the dead holder takeover, which reads the \
                    owner record",
        // Read on 2026-10-09 from the macOS, Linux x64, Windows x64 and Windows arm64 builds of
        // 2.1.294, whose code here is the same.
        verified_against: "2.1.294",
        depends: "lock::REFRESH, a switch, which holds it from before it reads the outgoing login \
                  a last time until it has recorded the login installed, so no session spends the \
                  refresh token of the copy it parks, `pitboard stow`, which holds it from its \
                  last reading of a login left in `.credentials.json` until the file is gone, so \
                  no session spends the refresh token of the login it parks, drops or renews \
                  meanwhile, and switch::refresh, which renews a login itself as this renewal \
                  does, the stored login where a change or a read must tell whose it is and its \
                  access token has lapsed, and the file's for `pitboard stow`: it reads the login \
                  again under the lock, tells one a session renewed meanwhile as it is, and saves \
                  the one it renewed as this save does, where the store still holds the token it \
                  sent, so a session that renews later reads the renewed login and sends its \
                  refresh token. None writes an owner record, so no session takes the lock over",
        probe: &[
            "\".oauth_refresh.lock\"),realpath:!1,stale:60000,update:5000",
            "tengu_oauth_refresh_legacy_lock_contended",
            "tengu_oauth_refresh_save_adopted_newer_write",
        ],
        absent: &[],
    },
    Assumption {
        name: "account_scoped_keys",
        fact: "signing in again deletes claudeAiOauth, organizationUuid, trustedDeviceToken, \
               enterpriseGateway and designOauth, and `/logout` clears the whole document but \
               writes back `coworkRemoteDevice`, so those five belong to the account",
        read_from: "the re-login prune and the logout path",
        verified_against: VERIFIED_AGAINST,
        depends: "switch::ACCOUNT_SCOPED, and what a park holds",
        probe: &[
            "claudeAiOauth",
            "organizationUuid",
            "trustedDeviceToken",
            "enterpriseGateway",
            "designOauth",
        ],
        absent: &[],
    },
    Assumption {
        name: "credential_cache",
        fact: "a running session serves the credential from a 30 second cache, so a swap is \
               picked up within about 33 seconds, only where no `.credentials.json` is behind \
               the keychain; see `fallback_file_pins_session_login`. The 30 seconds are \
               unchanged in 2.1.284; its re-checks after 1, 3 and 10 seconds, behind \
               `tengu_streamed_thimble` from 2.1.281, can only shorten that",
        read_from: "the keychain backend's cache, and measured against a running session",
        // The 33 seconds were measured against a running 2.1.278; nothing later was run.
        verified_against: "2.1.278",
        depends: "switch::ADOPTION_CEILING_SECONDS and Claude's adoption where nothing is \
                  behind the keychain, and autoswitch, which switches before a limit rather \
                  than at it so a session already running follows in time",
        probe: &[],
        absent: &[],
    },
    Assumption {
        name: "config_file_location",
        fact: "the config file is `<$CLAUDE_CONFIG_DIR or the home folder>/.claude<suffix>.json`, \
               but a legacy `<config dir>/.config.json` wins where it is a file, links followed, \
               that is not empty, or is empty and not read-only while `.claude<suffix>.json` is \
               missing. The two read `CLAUDE_CONFIG_DIR` differently: the \
               `.claude<suffix>.json` base is `CLAUDE_CONFIG_DIR || homedir()`, so an empty \
               value is the home folder, while the config dir is \
               `(CLAUDE_CONFIG_DIR ?? join(homedir(), \".claude\")).normalize(\"NFC\")`, so an \
               empty value makes it the empty path and `.config.json` is looked for in the \
               working directory",
        read_from: "the config path resolution, and the config dir the secure storage module \
                    shares",
        // Read on 2026-10-15 from the darwin-arm64, linux-x64, win32-x64 and win32-arm64 builds
        // of 2.1.299 and 2.1.300, and on 2026-10-16 from those of 2.1.301, whose code for both
        // paths is the same.
        verified_against: "2.1.301",
        depends: "claude::config_file and claude::config_dir, which read CLAUDE_CONFIG_DIR \
                  the two ways, home::check_absolute, which refuses an empty one, and \
                  claude::credman's read of the cached feature, from the file config_file names",
        probe: &[".config.json", "CLAUDE_CONFIG_DIR"],
        absent: &[],
    },
    Assumption {
        name: "settings_byte_order_mark",
        fact: "Claude Code reads a settings file, the person's `settings.json` and a managed \
               one alike, as UTF-16LE where it starts with FF FE and as UTF-8 otherwise, and \
               drops one U+FEFF at its start before it parses it, so the byte order mark \
               Windows PowerShell 5.1's `Set-Content -Encoding utf8` writes leaves its `env` \
               read. Whitespace before the mark, or a second mark, leaves the file unread. Its \
               config file's loader reads UTF-8 and drops one mark the same way, but the \
               Credential Manager gate (windows_backend_choice) parses that file as it is, so a \
               config that starts with a mark reads `tengu_windows_credman` as off while its \
               `env` still counts. A `.credentials.json` that starts with one is no login",
        read_from: "the settings file reader and its encoding sniff, safeParseJSON, the config \
                    loader, the backend gate's read of the config file and the plaintext \
                    store's read, in the darwin-arm64, linux-x64 and win32-x64 builds of \
                    2.1.302, read as bytes on 2026-10-18. Measured the same day on the session's \
                    PC, Windows 11 build 26300 on ARM64: with `CLAUDE_CODE_FORCE_WINDOWS_CREDMAN` \
                    1 in the `env` of a `settings.json` written that way, a new session of \
                    2.1.302 moved its login into Credential Manager",
        verified_against: "2.1.302",
        depends: "settings::own and the managed settings it reads, which drop the mark as \
                  Claude Code does and read the person's own UTF-16LE file, \
                  claude::paths::load_config and configfile::update, and credman::turned_on, \
                  which reads the config's `env` past a mark and its cached feature only from a \
                  config with none",
        probe: &[
            "safeParseJSON: invalid JSON (",
            r#"return"utf16le""#,
            r#"startsWith("\uFEFF")?e.slice(1):e}"#,
        ],
        absent: &[],
    },
    Assumption {
        name: "oauth_client",
        fact: "every login Claude Code stores was issued to client 9d1c250a-e61b-44d9-88ed-5944d1962f5e, \
               and a refresh answer without a refresh-token lifetime keeps the one it had",
        read_from: "the OAuth client id and the token refresh path",
        verified_against: VERIFIED_AGAINST,
        depends: "provider::claude::api::CLIENT_ID and provider::claude::document::renewed, and the renewal `pitboard stow` makes of a \
                  login left in `.credentials.json` whose access token has expired",
        probe: &["9d1c250a-e61b-44d9-88ed-5944d1962f5e"],
        absent: &[],
    },
    Assumption {
        name: "supervisor_daemon",
        fact: "a supervisor daemon outlives the session that started it, refreshes the login on \
               a timer of roughly eight hours, records itself in `<config dir>/daemon.lock`, and \
               writes through the same lock as everything else",
        read_from: "the daemon's auth scheduler and its lock file",
        verified_against: VERIFIED_AGAINST,
        depends: "daemon.rs, and the lock discipline the switch relies on",
        probe: &[
            "daemon.lock",
            "daemon.status.json",
            "auth: scheduling proactive refresh in ",
        ],
        absent: &[],
    },
    Assumption {
        name: "no_keyring_off_macos",
        fact: "Claude Code's code has no Secret Service, libsecret, gnome-keyring or KWallet \
               backend. Its storage backends are `keychain`, `plaintext` and `windows-credman`, \
               the last behind the `tengu_windows_credman` flag, and on Linux it uses \
               `plaintext` alone, so the login is the file. The `libsecret` in the Linux binary \
               belongs to the Bun runtime it ships in, behind `Bun.secrets`, which Claude \
               Code's code never calls",
        read_from: "the secure storage module's backend list and `getSecureStorage`, and the \
                    whole build searched for every Linux keyring name",
        verified_against: VERIFIED_AGAINST,
        depends: "host::linux, and Pitboard's claim that a parked login on Linux is no \
                  less protected than the live one",
        probe: &[
            "tengu_windows_credman",
            "CLAUDE_CODE_FORCE_WINDOWS_CREDMAN",
            r#"["keychain","plaintext","windows-credman"]"#,
        ],
        absent: &[
            "Bun.secrets",
            "org.freedesktop.secrets",
            "gnome-keyring",
            "SecretService",
        ],
    },
    Assumption {
        name: "sign_in_output",
        fact: "`claude auth login` writes `Opening browser to sign in…`, then `If the browser \
               didn't open, visit: <address>`, then `Paste code here if prompted > ` with no \
               newline, all to stdout and before it opens the browser, and from then on reads \
               a pasted `<code>#<state>` line from stdin. The address is the manual one: \
               `https`, on claude.com for a claude.ai login and platform.claude.com for a \
               Console one, coming back to platform.claude.com's page that shows the code. The \
               browser it opens goes to another address, which comes back to the loopback. \
               The address goes through a hyperlink helper, which writes it bare, or, where \
               its check says the terminal takes hyperlinks, as an OSC 8 hyperlink: \
               `ESC ] 8 ; ;`, the address, BEL, the address again as the link's text, bright \
               blue where colour is on, then `ESC ] 8 ; ;` and BEL. Piped, the check says yes \
               when `FORCE_HYPERLINK` is set to anything but 0 or nothing, which decides it \
               when set; otherwise when `NETLIFY` is set, `TERM_PROGRAM` or `LC_TERMINAL` is \
               ghostty, Hyper, kitty, alacritty, iTerm.app, iTerm2 or WarpTerminal, \
               `TERMINAL_EMULATOR` is JetBrains-JediTerm, `WT_SESSION` is set outside tmux, \
               `TERM_PROGRAM` is tmux 3.4 or later, or `TERM` contains kitty. A terminal \
               attached to a background session answers for the check instead, and \
               `auth login` has none",
        read_from: "the `auth login` command's OAuth flow and `startOAuthFlow`, the authorize \
                    address builder and its constants, the hyperlink helper the address is \
                    printed through with `assumeSupport`, and the supports-hyperlinks check \
                    it asks",
        // Read from a newer build than the rest of this register.
        verified_against: "2.1.289",
        depends: "provider::claude::engine's read_sign_in, provider::printed, which reads \
                  the hyperlink, and the address and code field the app's sign-in sheet \
                  offers",
        probe: &[
            "If the browser didn't open, visit: ",
            "Paste code here if prompted > ",
            r#"CLAUDE_AI_AUTHORIZE_URL:"https://"#,
            r#"CONSOLE_AUTHORIZE_URL:"https://"#,
            r#"MANUAL_REDIRECT_URL:"https://"#,
            // A sign-in address going through the hyperlink helper with `assumeSupport`, from
            // after the helper's name, which the minifier chooses. `auth login` makes one of
            // the three such calls in 2.1.289; 2.1.110 has none.
            "{assumeSupport:!0})}",
        ],
        absent: &[],
    },
    Assumption {
        name: "sign_in_takes_another_code",
        fact: "`claude auth login` reads each line typed back while it waits, trims it and \
               splits it at `#`. A line that is not `<code>#<state>` with both halves it \
               refuses by writing `Invalid code. Please make sure the full code was copied.` \
               and a newline to stderr, and it goes on reading in the same process, printing \
               no prompt again, so another line can be typed back. The first line with both \
               halves it takes, whatever its state half says. After that it still refuses a \
               line without both halves, and ignores one with them; a code the token exchange \
               then refuses ends the sign-in with `Login failed: ` on stderr and exit status 1",
        read_from: "the `auth login` command's handler for each line read from stdin, which \
                    trims and splits the line and refuses it before it looks at whether a code \
                    was taken, returns after the refusal and leaves the line reader open, and \
                    the OAuth service's `waitForAuthorizationCode` and \
                    `handleManualAuthCodeInput`, which takes a code only while its resolver is \
                    set, hands on the code half alone and clears the resolver",
        // Read on 2026-10-05 from the Windows arm64 build's sources and the macOS and Linux
        // builds of 2.1.289, all of which hold every literal below; 2.1.110 holds none.
        verified_against: "2.1.289",
        depends: "provider::claude::engine's refused_code, and the app model offering the \
                  code field again once Claude Code has refused a code",
        probe: &[
            // The refusal, then the handler returning rather than ending the process.
            "Invalid code. Please make sure the full code was copied.\n`);return}",
            "if(this.manualAuthCodeResolver)this.authorizationCodeReceived=!0",
        ],
        absent: &[],
    },
    Assumption {
        name: "install_places",
        fact: "the native installer puts its launcher at `~/.local/bin/claude`, and says so \
               when that directory is not on `PATH`; a global npm install puts `claude` in \
               npm's global `bin`, which is in Homebrew's prefix, `/opt/homebrew` or \
               `/usr/local`, or in `/usr/local` where nodejs.org's installer put Node, and \
               Claude Code tells it is one by `/node_modules/@anthropic-ai/` in the path the \
               running program resolves to; one whose path runs through a Homebrew \
               `Caskroom` is the Homebrew cask's",
        read_from: "the installation-method detection, which returns `npm-global` for an \
                    `execPath` holding `/node_modules/@anthropic-ai/` and lists \
                    `/opt/homebrew/bin` and `/usr/local/bin` among npm's places, \
                    `getHomebrewCaskName`, and the native install's PATH advice",
        // Read on 2026-10-05 from the macOS builds of 2.1.283 and 2.1.289 and the Linux build
        // of 2.1.289, all of which hold every literal below.
        verified_against: "2.1.289",
        depends: "claude::paths::install_places, where an app looks for `claude` after the \
                  login shell's PATH",
        probe: &[
            "Native installation exists but ~/.local/bin is not in your PATH",
            ".local/bin/claude",
            "/node_modules/@anthropic-ai/",
            "Detected Homebrew cask installation: ",
        ],
        absent: &[],
    },
    Assumption {
        name: "plaintext_credential_mode",
        fact: "the plaintext credential is written and then chmod'd to 0600, in its storage \
               directory, under the fixed name `.credentials.json`",
        read_from: "the plaintext backend's write path, which chmods 384 after writing",
        verified_against: VERIFIED_AGAINST,
        depends: "store::file, slot::CRED_FILE, and atomic::Perms::Secret matching what \
                  Claude Code itself writes",
        probe: &[
            ".credentials.json",
            "Warning: Storing credentials in plaintext.",
        ],
        absent: &[],
    },
    Assumption {
        name: "locked_keychain_keeps_last_login",
        fact: "a keychain read that fails, as one of a locked keychain does, leaves a session \
               serving the login it last read, cached again for another 30 seconds; a session \
               that has read none reads the keychain as empty and the file behind it. So while \
               the keychain is locked a running session keeps its account and follows no \
               switch, and one started then signs in with `.credentials.json` or is signed out",
        read_from: "the keychain backend's `read`, which logs the probe and serves its cache \
                    when `security` fails, and its `readAsync`, which keeps the cached login \
                    when `find-generic-password` exits 36 and answers absent",
        // Read on 2026-10-08 from the macOS builds of 2.1.291 to 2.1.294, whose code here is
        // the same.
        verified_against: "2.1.294",
        depends: "doctor's credential check, which says what a session does while the \
                  keychain is locked, and what `pitboard stow` says of a session that signed in \
                  with `.credentials.json`: signed out once the file is gone",
        probe: &["[keychain] read failed; serving stale cache"],
        absent: &[],
    },
    Assumption {
        name: "locked_sign_in_writes_fallback",
        fact: "a sign-in in a session that cannot read the keychain, and has not read its item, \
               fails the keychain write with exit 36, which is not transient there, writes the \
               login to `.credentials.json`, and leaves the keychain item as it was, because \
               the keychain read as empty before the write. The keychain then holds one login \
               and the file another",
        read_from: "the fallback wrapper's `update`, which deletes the primary after writing \
                    the fallback only when the primary read non-empty first, and the keychain \
                    backend's `update`, which calls exit 36 transient only once this process \
                    has seen the item; and measured on 2026-10-08 with 2.1.294 under a herdr \
                    server started over SSH: `/login` made a `.credentials.json` of mode 0600 \
                    twice, and the app's two switches after the first went on reading and \
                    writing the keychain item",
        verified_against: "2.1.294",
        depends: "doctor's fallback_login check, the warning every read and every change give \
                  about it, and `pitboard stow`, which puts that login away",
        probe: &["plaintext_fallback_used"],
        absent: &[],
    },
    Assumption {
        name: "fallback_outlives_keychain_writes",
        fact: "a keychain write that lands deletes `.credentials.json` only where the keychain \
               held nothing before it. Where both hold a login, every later keychain write, a \
               token refresh, a sign-in from a desktop session and a switch alike, leaves the \
               file's in place",
        read_from: "the fallback wrapper's `update`: after a primary write that lands it \
                    deletes the fallback only when its read of the primary before the write was \
                    null",
        verified_against: "2.1.294",
        depends: "doctor's fallback_login check and the warning every read and every change \
                  give while it is there, both of which name `pitboard stow`, the one thing \
                  that deletes the file",
        // Behaviour, with no literal of its own.
        probe: &[],
        absent: &[],
    },
    Assumption {
        name: "fallback_file_pins_session_login",
        fact: "before a session makes a request it looks at `.credentials.json` in its storage \
               directory, by its modification time alone. Where the file is not there, or the \
               look fails, it drops the login it holds and reads the store in front of the file, \
               the keychain or Credential Manager, through its 30 second cache. Where it is \
               there, whatever it holds and whether or not it can be read, the session keeps \
               the login it holds while that login is usable: it reads again where the file's \
               modification time has changed, at most every 30 seconds where that login is gone \
               or its refresh token is empty or known dead, after a 401, and at its own sign-in, \
               and 5 minutes before the login expires it reads the store and takes the login \
               there where it differs. So while the file sits behind that store, a session \
               already running keeps the account it is on after a switch until its login is \
               next renewed, or until it is started again. Read, not measured against a running \
               session",
        read_from: "the check before each API client, which stats the file, compares its \
                    modification time with the one it last saw, reads through the store's \
                    cache where the stat fails, and otherwise keeps a usable login, reading \
                    again at most every 30 seconds for one that is not; the renewal check 5 \
                    minutes before expiry, which reads the store first and takes its login \
                    where it differs; and the 401 handlers",
        // Read on 2026-10-08 from the macOS build and on 2026-10-09 from the Linux x64,
        // Windows x64 and Windows arm64 builds of 2.1.294, whose code here is the same, and
        // again on 2026-10-16 from both Windows builds of 2.1.301. On Linux the file is the
        // only store, and on Windows it is unless Credential Manager is turned on
        // (`windows_backend_choice`).
        verified_against: "2.1.294",
        depends: "Claude's adoption, by which a switch says that running sessions take it at \
                  their login's next renewal while the file is behind the keychain or \
                  Credential Manager; Claude's behind, which tells the file by a look and says \
                  nothing where the look fails; the fallback_login warning and check, which say \
                  so of a file with no login in it, or one Pitboard cannot read, too; and \
                  `pitboard stow`, which says sessions already running follow a switch within 33 \
                  seconds again once the file is gone",
        probe: &["lastCredentialsMtimeMs", "lastUnusableTokenRecheckAt"],
        absent: &[],
    },
    Assumption {
        name: "login_is_one_organisation",
        fact: "a login is one account in one organisation. Anthropic's account uuid is the \
               person, and a sign-in is made for one of their organisations: the authorize \
               address takes `orgUUID`, and with `forceLoginOrgUUID` set a token whose \
               organisation `/api/oauth/validate` names otherwise is refused. Claude Code tells \
               logins apart by `accountUuid` and `organizationUuid` together, so a sign-in to \
               another organisation of the same person is an `account_switch`. Usage is asked \
               with the token alone, so it is that organisation's",
        read_from: "the sign-in's authorize address and its forceLoginOrgUUID check, the \
                    comparison a sign-in and `/login` make of the account before and after, \
                    and the usage request's headers",
        verified_against: "2.1.294",
        depends: "state::Account::owned_by and state::new_id: which enrolled account a login \
                  is, and what a new one is filed under",
        probe: &[
            "searchParams.append(\"orgUUID\"",
            "Your authentication token belongs to organization",
            "\"same_account\":\"account_switch\"",
            "Account changed via /login",
        ],
        absent: &[],
    },
    Assumption {
        name: "config_may_name_no_organisation",
        fact: "`oauthAccount` names the organisation of the login of the process that wrote \
               it, from the profile, whose shape requires `organization.uuid`. A sign-in that \
               could not read the profile writes it from the token's own account instead, \
               whose organisation can be absent, and Claude Code then reads none",
        read_from: "the sign-in's fallback to `tokenAccount` when the profile cannot be \
                    fetched, and the readers that stand `acct:` in for a missing organisation",
        verified_against: "2.1.294",
        depends: "Claude's own_record, which names such an account with no organisation, as \
                  the config wrote it, so in_use::known tells a change of it by the same id; \
                  and Claude's name, which takes the email the record holds for the account \
                  and organisation validate named, where the profile is refused",
        probe: &["tokenAccount", "acct:${"],
        absent: &[],
    },
    Assumption {
        name: "usage_cache_stamp_is_the_configs",
        fact: "`cachedUsageUtilization` is written after `GET /api/oauth/usage`, which a \
               session asks with the login it holds, and stamped with the config's \
               `accountUuid`, read before the request and again before the write. Nothing \
               compares the stamp with the login: a session still holding the login it had \
               before a switch writes that login's numbers under the account the config names \
               since. Claude Code's own readers compare the stamp with the config alone, and \
               clear the cache where the two differ. A sign-in clears it, as a logout does",
        read_from: "the usage read, which takes the config's `accountUuid` before it asks with \
                    the session's credentials, the cache's writer, which writes only where the \
                    config still names that account, the cache's two readers, and the reset \
                    a sign-in and a logout both run",
        // Read on 2026-10-08 from the macOS build and on 2026-10-09 from the Linux x64,
        // Windows x64 and Windows arm64 builds of 2.1.294, whose code here is the same.
        verified_against: "2.1.294",
        depends: "status, which takes no usage from it, and the automatic switch, which \
                  decides from status's offline rows",
        probe: &[
            "cachedUsageUtilization:{fetchedAtMs:Date.now(),",
            "cachedUsageUtilization=void 0",
        ],
        absent: &[],
    },
    Assumption {
        name: "status_reads_the_config_usage_the_token",
        fact: "`/status` prints the config's `oauthAccount` email and organisation, and a \
               running session reads the config again within about a second of a change. \
               `/usage` shows the session's header snapshot, or without one the config's usage \
               cache where its stamp is the config's account and it is under an hour old; it \
               then answers from that cache where it is under 60 seconds old and newer than \
               the session's last header reading, and otherwise asks with the login the \
               session holds. So a session that has not taken a switch names the account \
               switched to in `/status`, while `/usage` and every request go on with the login \
               it holds",
        read_from: "`/status`'s account rows, which read the config's `oauthAccount`; the \
                    config's freshness watch and its 1000 ms poll; the `/usage` screen, which \
                    seeds itself from the session's header snapshot or the cache, and the \
                    usage read it starts",
        // Read on 2026-10-08 from the macOS build and on 2026-10-09 from the Linux x64,
        // Windows x64 and Windows arm64 builds of 2.1.294, whose code here is the same.
        verified_against: "2.1.294",
        depends: "what a switch says of sessions already running while a file is behind the \
                  keychain, which names the file and no account: `/status` in one of them \
                  names the account switched to",
        probe: &["Usage read answered from a snapshot"],
        absent: &[],
    },
    Assumption {
        name: "config_identity_is_the_last_writers",
        fact: "`oauthAccount`'s account, email and organisation are written by a sign-in, from \
               the profile or else the token's own account, and at a process start where \
               `profileFetchedAt` is missing or over 24 hours old or a profile field is \
               missing, from the profile of that process's own token. Every start of a \
               process signed in to claude.ai also writes the email and organisation that \
               `/api/claude_cli/bootstrap` gives for that process's token, where it names the \
               config's account or none. A token refresh writes no identity field. So the \
               config can name another account than the login stored: a process started on \
               another login writes that login's account where the profile was missing, \
               incomplete or a day old, and one started on another organisation of the same \
               person writes that organisation",
        read_from: "the sign-in's profile and `tokenAccount` write, the start-up profile fetch \
                    and its 24 hour check, the bootstrap merge into `oauthAccount`, and the \
                    refresh's profile update",
        // Read on 2026-10-08 from the macOS build and on 2026-10-09 from the Linux x64,
        // Windows x64 and Windows arm64 builds of 2.1.294, whose code here is the same.
        verified_against: "2.1.294",
        depends: "in_use::known, which takes the config naming another account than when \
                  Anthropic last named the login stored as a sign that something signed in, \
                  never as whose the login is; and every reader of the account in use, which \
                  takes it from that record and not from the config",
        probe: &["profileFetchedAt:Date.now()", "organization_uuid!=null)"],
        absent: &[],
    },
    Assumption {
        name: "status_line_input_names_no_account",
        fact: "the status line's input holds `rate_limits.five_hour` and `seven_day`, each \
               `used_percentage` and `resets_at`, taken whole from one response's headers and \
               only for windows whose reset is ahead, and no account, organisation or email. \
               It is emptied only by that process's own account change, a sign-in or \
               sign-out, by the end of a remote attach after one, or by a response while the \
               process holds no claude.ai login. A response to a request sent before the \
               process's account changed is dropped, and so is a reading older than the last \
               one applied. So a session passes the numbers of the login it holds, whatever the \
               config names, and both windows it passes are of one response",
        read_from: "the status line's input and the hook fields common to every hook; the \
                    session's header snapshot, which keeps only windows whose reset is ahead; \
                    the limits' `applyWindowReadings`, which sets that snapshot whole from one \
                    response; `resetCurrentLimits` and both its callers; and the header \
                    reader, which drops a response from before an account change and empties \
                    the snapshot without a claude.ai login",
        // Read on 2026-10-08 from the macOS build and on 2026-10-09 from the Linux x64,
        // Windows x64 and Windows arm64 builds of 2.1.294, whose code here is the same.
        verified_against: "2.1.294",
        depends: "statusline::read, which takes a session's numbers to be the account's whose \
                  reading, as Anthropic answered it, holds their windows, never the account \
                  Claude Code's config names, and takes a limit's next window on the word of \
                  the other window passed with it",
        probe: &[
            ".spend_limit)&&{rate_limits:",
            "applyWindowReadings",
            "resetCurrentLimits",
        ],
        absent: &[],
    },
    Assumption {
        name: "login_carries_its_plan",
        fact: "the stored login keeps its plan in `claudeAiOauth`, beside the tokens: \
               `subscriptionType`, which a sign-in takes from the profile's \
               `organization.organization_type`, so `claude_max`, `claude_pro`, `claude_team` \
               and `claude_enterprise` are stored as `max`, `pro`, `team` and `enterprise`, and \
               `rateLimitTier`, the profile's `organization.rate_limit_tier` as Anthropic gives \
               it, such as `default_claude_max_20x` or `default_claude_max_5x`. Each is null \
               where neither the profile nor the login stored before says one. Every save into \
               `claudeAiOauth` keeps the stored value of either where the new login has none, \
               and a token refresh asks for the profile again only where the login lacks \
               either or the config's `oauthAccount` lacks one of its profile fields. So a \
               plan changed since the login's last sign-in can go unseen in it",
        read_from: "the profile read, which maps `organization_type` through a table and takes \
                    `rate_limit_tier` whole; the function every save of the login into \
                    `claudeAiOauth` goes through, which writes both beside the tokens; the \
                    sign-in's save; and the refresh's check of whether to ask for the profile. \
                    Measured on 2026-10-10 in the stored logins of five accounts on one Mac: \
                    `max` with `default_claude_max_20x`, and `team` with `default_claude_max_5x`",
        // Read on 2026-10-11 from macOS 2.1.294 to 2.1.296, and Linux x64 and Windows 2.1.296.
        verified_against: "2.1.296",
        depends: "provider::claude::document::plan, which reads the plan off a login or a park \
                  of it for `pitboard status`, the app and the account's record, as \
                  `subscriptionType` capitalised and the multiple `rateLimitTier` ends with",
        probe: &[
            r#"[["claude_max","max"],["claude_pro","pro"],["claude_enterprise","enterprise"],["claude_team","team"]]"#,
            "organization?.rate_limit_tier??null",
            "?.subscriptionType??null,rateLimitTier:",
        ],
        absent: &[],
    },
    Assumption {
        name: "windows_home_folder",
        fact: "on Windows the home `~/.claude` and `~/.claude.json` are in is `os.homedir()`: \
               `USERPROFILE` where it is set, and `GetUserProfileDirectoryW` where it is not. \
               A `USERPROFILE` shorter than three bytes makes it throw. `HOME`, which Git Bash \
               sets, is not read for it",
        read_from: "the config dir, `(CLAUDE_CONFIG_DIR ?? join(homedir(), \".claude\"))` in NFC \
                    with `homedir` from `os`, in the win32-x64 and win32-arm64 builds; Bun \
                    1.4.3's `os.homedir` in src/runtime/node/node_os.rs, as embedded in Claude \
                    Code 2.1.300, which calls libuv's `uv_os_homedir`, in src/win/util.c at \
                    oven-sh/libuv 8023581; read on 2026-10-15",
        verified_against: "2.1.300",
        depends: "context::Environment::home and host::windows::user::home, which every \
                  default home on Windows is made from",
        probe: &[
            "import{homedir as",
            "USERPROFILE",
            "GetUserProfileDirectoryW",
        ],
        absent: &[],
    },
    Assumption {
        name: "windows_open_sharing",
        fact: "on Windows Claude Code opens its files sharing their reading, writing and \
               deletion, so a POSIX rename lands over one it holds open. It writes \
               `.credentials.json` by staging `<file>.tmp.<8 hex>` beside it and renaming that \
               over, trying the rename four times 50ms apart on EPERM, EBUSY and EACCES, and \
               then rewriting the file in place on EXDEV, EPERM, EEXIST or EBUSY. What its \
               strict read makes of a file held without sharing is \
               windows_plaintext_strict_read's",
        read_from: "Bun 1.4.3's `openat_windows`, which calls `NtCreateFile` with \
                    `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`, in src/sys/lib.rs \
                    at bun-v1.4.3, its errno table, which makes a sharing violation EBUSY, in \
                    src/errno/windows_errno.rs, and libuv's `uv_fs_open`, which shares all three but under \
                    `UV_FS_O_EXLOCK`, in src/win/fs.c at oven-sh/libuv 8023581, as embedded in \
                    Claude Code 2.1.300; the file write's staging, its retries and its \
                    in-place arm, in the win32-x64 and win32-arm64 builds; read on 2026-10-15. \
                    Measured with Process Monitor and 2.1.302 on the session's PC, Windows 11 \
                    build 26300 on ARM64, on 2026-10-18: `claude.exe` opened `.credentials.json` \
                    and `.claude.json` sharing all three, renamed `.claude.json.tmp.<pid>.<hex>` \
                    over `.claude.json`, and no open of a file in `.claude` met a sharing \
                    violation or access denied while the app and the command line switched",
        verified_against: "2.1.300",
        depends: "host::windows::fs::rename, which renames over Claude Code's files the POSIX \
                  way where the volume has it and waits out a file held without sharing its \
                  deletion, and Pitboard's reads of Claude Code's files, which share all three \
                  as std opens a file and are never held across Claude Code's lock: a read \
                  not sharing deletion would turn Claude Code's write into an in-place one",
        probe: &[
            r#"["EXDEV","EPERM","EEXIST","EBUSY"]"#,
            r#"["EPERM","EBUSY","EACCES"]"#,
            "refusing the in-place arm on a non-regular target",
        ],
        absent: &[],
    },
    Assumption {
        name: "windows_install_layout",
        fact: "on Windows the native installer keeps its launcher as a copy, `claude.exe` in \
               `(HOME ?? homedir())\\.local\\bin`, put in place by a rename once its checksum \
               matches, and each version in `claude\\versions` of `XDG_DATA_HOME`, else of \
               `<home>\\.local\\share`. A global npm install's program is `bin/claude.exe`, which npm's \
               cmd-shim starts as it is from `claude.cmd` in its prefix, `%APPDATA%\\npm` by \
               default, and which postinstall links or copies from \
               `@anthropic-ai/claude-code-win32-<arch>/claude.exe`; without that it is a stub \
               that prints how to install. winget's `Anthropic.ClaudeCode` is the bare \
               portable `claude.exe`, linked in winget's `Links`",
        read_from: "the native installer's paths, its launcher's update and its `claude.exe` on \
                    win32, in the win32-x64 and win32-arm64 builds; `install.cjs` and \
                    `package.json` of `@anthropic-ai/claude-code@2.1.300`; the installer \
                    manifest of `Anthropic.ClaudeCode` 2.1.292, the newest in \
                    microsoft/winget-pkgs; read on 2026-10-15",
        verified_against: "2.1.300",
        depends: "claude::paths::install_places and host::Os::package_bins, where an app looks \
                  for `claude.exe` after the registry's `Path`",
        probe: &["Refusing to install a launcher copy of "],
        absent: &[],
    },
    Assumption {
        name: "windows_version_resource",
        fact: "on Windows `claude.exe` carries a version resource whose product version, fixed \
               and as text, is the release with a fourth part of 0, `2.1.300.0` for 2.1.300, \
               with the product name `Claude Code`",
        read_from: "the version resource of the win32-x64 and win32-arm64 builds of 2.1.300, \
                    read as bytes on 2026-10-15",
        verified_against: "2.1.300",
        depends: "claude::paths::installed_version, which reads the release there on Windows",
        probe: &[],
        absent: &[],
    },
    Assumption {
        name: "windows_managed_settings",
        fact: "on Windows Claude Code takes managed settings from `C:\\Program Files\\ClaudeCode`, \
               its `managed-settings.json` and the `.json` files of `managed-settings.d`, and \
               from the value `Settings` of `HKLM\\SOFTWARE\\Policies\\ClaudeCode`, a `REG_SZ` or \
               a `REG_EXPAND_SZ` as `reg.exe query` prints it, its end trimmed, which it takes \
               over the folder unless `managedSourcesBehavior` merges them. The same value \
               under `HKCU` it reads only where neither holds a policy, and there it ignores \
               what only an administrator may set. A source that holds nothing but \
               `managedSourcesBehavior` or `wslInheritsWindowsSettings` is not a policy and \
               keeps `HKCU` out of nothing (its `pt`). A failed `HKLM` `reg.exe query` it treats \
               as absent (its `vYn` nulls the machine output on a non-zero exit) and still reads \
               `HKCU`, while an unreadable managed file keeps `HKCU` out",
        read_from: "the managed folder, the policy keys, their reading through \
                    `%SystemRoot%\\System32\\reg.exe query <key> /v Settings` and the managed \
                    sources' order in the win32-x64 and win32-arm64 builds of 2.1.300, read as \
                    bytes on 2026-10-15",
        verified_against: "2.1.300",
        depends: "settings::overrides, custom_oauth and overrides_of_a_run_in, through \
                  host::Host::policy and administered_folder, where holds_policy and managed \
                  keep HKCU out",
        probe: &[
            "C:\\Program Files\\ClaudeCode",
            "SOFTWARE\\Policies\\ClaudeCode",
            "managedSourcesBehavior",
            "wslInheritsWindowsSettings",
        ],
        absent: &[],
    },
    Assumption {
        name: "windows_daemon_identity",
        fact: "on Windows the supervisor daemon records in `daemon.lock`, beside its pid, \
               `procStartFt`: its own creation time from `GetProcessTimes`, called through \
               `bun:ffi` with `PROCESS_QUERY_LIMITED_INFORMATION`, the FILETIME as a decimal \
               string. It takes a daemon for running only while its pid's creation time, read \
               the same way, is the same text, so a pid Windows has given another process \
               reads as gone. Where `bun:ffi` does not load it writes `procStart`, \
               PowerShell's `CreationDate.Ticks`, which it never compares with a FILETIME. \
               `daemon.lock` holds no `pidDomain`, which only the session records carry, as \
               `win32:<host name>`. Its pipes are `\\\\.\\pipe\\cc-daemon-<the 16 hex of \
               daemon\\pipe.key>-<name>`",
        read_from: "the daemon lock's writer and its liveness check, the process start module \
                    and the daemon's pipe names, in the win32-x64 and win32-arm64 builds of \
                    2.1.300, read as bytes on 2026-10-16; 2.1.301's win32-x64 build is the same",
        verified_against: "2.1.300",
        depends: "claude::daemon's running, through host::proc::started",
        probe: &["procStartFt", "\\\\.\\pipe\\cc-daemon-"],
        absent: &[],
    },
    Assumption {
        name: "windows_backend_choice",
        fact: "on Windows Claude Code keeps its login in Credential Manager, with \
               `.credentials.json` behind it, where `CLAUDE_CODE_FORCE_WINDOWS_CREDMAN` is \
               exactly `1` in its environment, which it asks on every store access, or where its \
               config file caches `tengu_windows_credman` as true, which it reads once a process: \
               a non-empty `<config dir>\\.config.json`, else `<CLAUDE_CONFIG_DIR || home>\\.claude.json` \
               where `.config.json` is missing, empty, a folder or a link loop, any other error \
               or a file that is not JSON reading as false. Otherwise the file alone is its \
               store. The variable reaches a session from the environment it starts with, which \
               Windows makes for one started from Explorer from `HKCU\\Environment` over the \
               machine's `Session Manager\\Environment`, and from \
               the `env` of the config file, of the person's `settings.json` and of managed \
               settings, assigned in that order at start-up, each value coerced to a string. A \
               project's or local settings' `env` passes only a safe list then, which leaves the \
               variable out, but is assigned whole once the folder is trusted, or in print \
               mode, so a project can turn the store on for its own sessions from then on",
        read_from: "`getSecureStorage`, the backend gate beside it with its memo and its read \
                    of the cached feature, the backend list, and \
                    applySafeConfigEnvironmentVariables, applyConfigEnvironmentVariables, the \
                    project-scope filter and the safe list, in the win32-x64 and win32-arm64 \
                    builds of 2.1.301, read as bytes on 2026-10-16",
        verified_against: "2.1.301",
        depends: "claude::credman::turned_on, which reads every signal but a project's and a \
                  `--settings` file's, managed settings only from the sources a session takes \
                  (settings::env_settings), and live::chain on Windows, which is the file only \
                  where none is on and Credential Manager holds nothing at the live target, so \
                  a session a project turned the store on for is caught once it wrote there",
        probe: &[
            "tengu_windows_credman",
            "CLAUDE_CODE_FORCE_WINDOWS_CREDMAN",
            r#"["keychain","plaintext","windows-credman"]"#,
            "applySafeConfigEnvironmentVariables",
        ],
        absent: &[],
    },
    Assumption {
        name: "windows_plaintext_strict_read",
        fact: "on Windows Claude Code reads `.credentials.json` strictly before it writes: a \
               missing file, a folder in its place or a path through a file is no login, a \
               file that is not JSON is no login either, and any other error is a failed read, \
               EACCES, EPERM and a sharing violation's EBUSY included, which skips its write \
               (`read_failed_skip_write`). So a read that held the file without sharing it \
               would have Claude Code drop a renewal",
        read_from: "the plaintext store's strict read and its error mapping, called with \
                    `win32`, and the store mutation that skips its write on a failed read, in \
                    the win32-x64 and win32-arm64 builds of 2.1.301, read as bytes on 2026-10-16",
        verified_against: "2.1.301",
        depends: "Pitboard's reads of Claude Code's files on Windows, which share reading, \
                  writing and deletion as std opens a file and are never held across Claude \
                  Code's lock (windows_open_sharing)",
        probe: &["read_failed_skip_write"],
        absent: &[],
    },
    Assumption {
        name: "windows_plaintext_write",
        fact: "on Windows Claude Code writes `.credentials.json` in its storage folder, making \
               the folder first, by a temporary file renamed over it (windows_open_sharing), \
               then `chmod` 0600, which there only clears the read-only attribute. It sets no \
               access list, so a file it makes takes its folder's inherited one. Its rename is \
               `MoveFileExW` with `MOVEFILE_REPLACE_EXISTING`, which fails over a read-only \
               file, and its in-place arm cannot open one to write, so its write fails there. \
               The file's modification time changes with every write",
        read_from: "the plaintext store's write, which calls the file write helper with 384 \
                    and then chmod 384, in the win32-x64 and win32-arm64 builds of 2.1.301; Bun \
                    1.4.3's `chmod` and `rename` in src/sys/sys_uv.rs and \
                    src/runtime/node/node_fs.rs, which call libuv's `uv_fs_chmod`, \
                    `_wchmod`, and `uv_fs_rename`, `MoveFileExW`, in src/win/fs.c at \
                    oven-sh/libuv 8023581, as embedded in Claude Code 2.1.301; read on \
                    2026-10-16",
        verified_against: "2.1.301",
        depends: "store::file on Windows, which writes the file by a temporary file renamed \
                  over it, keeps the access list it had (host::windows::fs::copy_access) and \
                  refuses a read-only one, as Claude Code's own write fails there",
        probe: &[
            ".credentials.json",
            "Warning: Storing credentials in plaintext.",
        ],
        absent: &[],
    },
    Assumption {
        name: "windows_file_adoption",
        fact: "on Windows, where `.credentials.json` is Claude Code's store, a running session \
               looks at its modification time before each request and reads it again where it \
               has changed, so a switch, which changes it, is taken at the session's next \
               request",
        read_from: "the check before each API client, which compares the file's modification \
                    time with the one it last saw, in the win32-x64 and win32-arm64 builds of \
                    2.1.301, read as bytes on 2026-10-16. Measured with 2.1.302 on the session's \
                    PC, Windows 11 build 26300 on ARM64, on 2026-10-18: a running `claude` \
                    answered its next prompt as the account switched to, 11 s after a switch from \
                    the app's menu, and 21 s after one from a terminal, its answer at 11 s still \
                    under way",
        verified_against: "2.1.302",
        depends: "Claude's adoption on Windows, which says sessions follow within 33 seconds, \
                  as on Linux",
        probe: &["lastCredentialsMtimeMs"],
        absent: &[],
    },
    Assumption {
        name: "credman_account_name",
        fact: "on Windows Claude Code names its Credential Manager item's user \
               `claude-code-user` whoever runs it, so its item is \
               `Claude Code-credentials[-<8 hex>]/claude-code-user` as `Bun.secrets` names one, \
               and the pieces of one too large for an item `#<k>`, `#m` and `#p` after it",
        read_from: "the secure storage module's account name, a literal in the win32-x64 and \
                    win32-arm64 builds of 2.1.301 where the others read `USER`, and the store \
                    that passes it to `Bun.secrets` with the service name; Bun 1.4.3's \
                    `Bun.secrets` in src/jsc/bindings/SecretsWindows.cpp, which names a \
                    generic item `<service>/<name>`, as embedded in Claude Code 2.1.301; read on \
                    2026-10-16",
        verified_against: "2.1.301",
        depends: "slot::account_name on Windows, and claude::credman's look at the live target \
                  and its manifest",
        probe: &["claude-code-user", "Bun.secrets.get"],
        absent: &[],
    },
    Assumption {
        name: "credman_target",
        fact: "on Windows Claude Code keeps its login in the Credential Manager item \
               `<service>/claude-code-user`, the service named as on macOS: \
               `Claude Code${OAUTH_FILE_SUFFIX}-credentials`, with \
               `-<first 8 hex of sha256>` of `CLAUDE_SECURESTORAGE_CONFIG_DIR`, else of the NFC \
               config dir, appended unless the one asked for is unset. The string is hashed as \
               given, so `C:\\x`, `C:/x`, `c:\\x` and `C:\\x\\` name four items for one folder, and \
               any `CLAUDE_CONFIG_DIR` set names one of its own",
        read_from: "the secure storage module's service name and slot derivation, and the \
                    Credential Manager store that hands it to `Bun.secrets` with the user name, \
                    in the win32-x64 and win32-arm64 builds of 2.1.301, read as bytes on \
                    2026-10-16",
        verified_against: "2.1.301",
        depends: "slot::service_for_dir and slot::dir_hash, which hash the string as given, \
                  claude::paths::live_service, and claude::credman::Store",
        probe: &[
            "-credentials",
            "OAUTH_FILE_SUFFIX",
            "CLAUDE_SECURESTORAGE_CONFIG_DIR",
        ],
        absent: &[],
    },
    Assumption {
        name: "credman_chunking",
        fact: "a login of up to 2400 bytes is the item's whole value. Above that it is base64 in \
               pieces of 2400 characters, `#0` to `#n-1` after the user name, at most 256, with \
               `#m` holding `{\"n\":n,\"l\":length}` and `#p` the number of pieces a write may \
               leave. A write of pieces sets `#p`, each piece, then `#m`, then deletes the item \
               and any piece past the last, then `#p`; a write of one item over pieces sets the \
               item, `#p`, deletes `#m`, each piece, then `#p`. A read takes `#m` only where its \
               `n` and `l` are integers with 0 < n <= 256 and 0 < l <= n * 2400, else reads the \
               item, never reads `#p`, and fails where a piece is missing or the pieces' length \
               is not `l`. More than 256 pieces, 460800 bytes, fails the write \
               (`credman_oversize`). A delete takes the item, the pieces `#m` names and `#m`, \
               then as many as `#p` says, or where it says none each piece found from `#0` on. \
               An item holding nothing is none. Pieces replaced in place under a manifest of \
               the same length can be read half old and half new",
        read_from: "the Credential Manager store's write, read, delete and manifest check, in the \
                    win32-x64 and win32-arm64 builds of 2.1.301, read as bytes on 2026-10-16",
        verified_against: "2.1.301",
        depends: "claude::pieces, which writes in the same order, adds a manifest no base64 \
                  length matches while pieces are replaced in place, and reads a manifest Claude \
                  Code would pass over, or `#p` beside one, as unreadable; claude::credman::Store, \
                  and the ceiling a switch and a renewal check before they write",
        probe: &[
            "SecureStorageData too large for Credential Manager (",
            "chunked read inconsistent: got ",
            "credman_oversize",
        ],
        absent: &[],
    },
    Assumption {
        name: "credman_probe",
        fact: "a session asks `Bun.secrets` for its item once as it starts, for at most 10 \
               seconds. Where that fails or takes longer, Credential Manager is out of its reach \
               for the session's life: it reads only `.credentials.json` and every write to the \
               store fails, which its fallback wrapper answers by writing the file. Each later \
               call has a 2 second timeout",
        read_from: "the startup probe, its 10 second timeout and the flag every Credential \
                    Manager read and write asks, in the win32-x64 and win32-arm64 builds of \
                    2.1.301, read as bytes on 2026-10-16",
        verified_against: "2.1.301",
        depends: "credman_unreachable_writes_file, by which such a session leaves a login in \
                  the file behind Credential Manager",
        probe: &["probe timeout", "[windows-credman] load failed: "],
        absent: &[],
    },
    Assumption {
        name: "credman_migration",
        fact: "with Credential Manager on, every write goes there first. Where it took the login \
               and held none before, `.credentials.json` is deleted, a failure to delete \
               ignored; where it held one, the file is left. Where Credential Manager fails, \
               the login is written to the file (`plaintext_fallback_used`), and where it held \
               a login it cannot then delete, the write fails though the file took it \
               (`plaintext_fallback_primary_not_cleared`); both failing is \
               `primary_and_fallback_failed`. A strict read takes an entry that is not JSON as a \
               failed read. The successor backend behind `tengu_hover_rest` is not used where no \
               backend is handed in, as an ordinary `claude` hands none",
        read_from: "the fallback wrapper's update, delete and strict read around the Credential \
                    Manager and plaintext stores, and `getSecureStorage`, in the win32-x64 and \
                    win32-arm64 builds of 2.1.301, read as bytes on 2026-10-16",
        verified_against: "2.1.301",
        depends: "store::Live::taken_over_by_the_first, which writes Credential Manager and \
                  deletes the file only where it held nothing, and never writes the file where \
                  Credential Manager fails: the journal's recovery takes that",
        probe: &[
            "plaintext_fallback_used",
            "primary_and_fallback_failed",
            "plaintext_fallback_primary_not_cleared",
        ],
        absent: &[],
    },
    Assumption {
        name: "credman_move_lost_a_piece",
        fact: "Claude Code's move of a login into Credential Manager can leave pieces no read \
               can join, and the login with them. Measured with 2.1.302 on 2026-10-18 on the \
               session's PC, Windows 11 build 26300 on ARM64: `CLAUDE_CODE_FORCE_WINDOWS_CREDMAN` \
               turned on, a session moved a 524-byte `.credentials.json` login there as `#m`, 16 \
               bytes naming 3 pieces, beside `#0` and `#1` of 2400 bytes each, with no `#2`, no \
               `#p` and no item of its own, and deleted the file; a new session said \
               \"Not logged in\". With those items there, `claude auth login` said it was \
               logged in and wrote nothing. `claude auth logout` deleted every item, and a \
               sign-in after it wrote one item of 524 bytes and no file. Read in the code: what \
               it moves is its whole storage object, written whole up to 2400 bytes and above \
               that as base64 pieces; no single write names a piece it lacks, while the 2-second \
               timeout on each call and its lock's stale and re-entered holds can let two \
               overlap; its login skips its write after a strict read that failed, and its \
               logout deletes the pieces a manifest names and every one found from `#0` on",
        read_from: "the Credential Manager store's write, read and delete, the fallback wrapper's \
                    update, `claude auth login`'s save and `claude auth logout`, in the \
                    win32-x64 build of 2.1.302, read as bytes on 2026-10-18, and the session's \
                    `credman-names` reports of steps 7 and 8",
        verified_against: "2.1.302",
        depends: "claude::pieces::read, which takes pieces no read can join for a broken login \
                  (store::Error::Broken), and doctor's credential check and every refusal over \
                  it, which say Claude Code cannot read it either and that `claude auth logout`, \
                  then a sign-in, clears it",
        probe: &[
            "logout: secure storage could not be read",
            "Couldn't save your login. Try logging in again.",
        ],
        absent: &[],
    },
    Assumption {
        name: "credman_unreachable_writes_file",
        fact: "with Credential Manager on, a session whose start-up look at `Bun.secrets` \
               failed, as every call fails in a network logon, reads no login there and fails \
               every write there, so its fallback wrapper writes the login, a sign-in's or a \
               renewal's, to `.credentials.json` (`plaintext_fallback_used`) and leaves the \
               Credential Manager item as it was: it deletes that only where its read before \
               the write held a login. Credential Manager then holds one login and the file \
               another, and a session that reads only the file signs in with the file's",
        read_from: "the Credential Manager store's read and update, which answer no login and a \
                    failed write while the start-up probe has not passed, the probe, and the \
                    fallback wrapper's update and read, in the win32-x64 and win32-arm64 builds \
                    of 2.1.301, read as bytes on 2026-10-16",
        verified_against: "2.1.301",
        depends: "Claude's behind on Windows, doctor's fallback_login check, the warning every \
                  read and every change give while the file is there, and `pitboard stow`, \
                  which puts that login away",
        // Behaviour, with no literal of its own.
        probe: &[],
        absent: &[],
    },
    Assumption {
        name: "credman_attributes",
        fact: "`Bun.secrets` writes each item generic, named `<service>/<name>`, the name its \
               user name, the value's UTF-8 bytes its blob, with enterprise persistence, no \
               comment and no attributes: every logon of the person on that PC reads it, and a \
               roaming profile takes it to others. It reads an item holding nothing as one that \
               is there",
        read_from: "Bun 1.4.3's `Bun.secrets` in src/jsc/bindings/SecretsWindows.cpp, as embedded \
                    in Claude Code 2.1.301, read on 2026-10-16, and RF-G3, which wrote one as \
                    Claude Code 2.1.300 calls it (run 38704825885)",
        verified_against: "2.1.301",
        depends: "host::windows::credman, which writes each item the same way, \
                  `credman_write_is_harmless`, and doctor's credential store line",
        probe: &["Bun.secrets.set", "Bun.secrets.delete"],
        absent: &[],
    },
    Assumption {
        name: "windows_credman_cache",
        fact: "a running session serves the login it read from Credential Manager for 30 \
               seconds before it reads it again, and keeps the last one it read where a read \
               fails. Read, not measured against a running session",
        read_from: "the Credential Manager store's cached read, in the win32-x64 and \
                    win32-arm64 builds of 2.1.301, read as bytes on 2026-10-16",
        verified_against: "2.1.301",
        depends: "Claude's adoption on Windows, 33 seconds as on macOS until session step 7 \
                  measures it",
        probe: &["[windows-credman] strict load failed: ", "readInFlight"],
        absent: &[],
    },
    Assumption {
        name: "windows_browser_open",
        fact: "on Windows `claude auth login` opens its sign-in page once it has printed its \
               prompt by starting `rundll32 url,OpenURL <address>`, a child of its own, which \
               hands the address to the shell; where its settings or `BROWSER` name a program, \
               it starts that with the address instead. A browser that was not running starts \
               from inside whatever job Claude Code runs in",
        read_from: "the opener `startOAuthFlow` calls after its prompt, which checks the address \
                    is `http` or `https` and starts `rundll32` on win32, in the win32-x64 and \
                    win32-arm64 builds of 2.1.300, read as bytes on 2026-10-16",
        verified_against: "2.1.300",
        depends: "host::windows::job, which ends only the tool's own programs on a cancel, so a \
                  browser its sign-in opened keeps running (RF-F2)",
        probe: &["[\"url,OpenURL\","],
        absent: &[],
    },
    Assumption {
        name: "windows_token_handover",
        fact: "on Windows Claude Code reads no token handed to it on a descriptor. Its reader of \
               `CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR` reads the descriptor directly only where \
               `fstat` calls it a socket; otherwise it opens `/proc/self/fd/<n>`, and where that \
               fails it reads the well-known file `/home/claude/.claude/remote/.oauth_token` \
               before it reads directly, which it does only on EACCES or EPERM. It reads that \
               file, a regular file alone, where the variable is unset too, unless it runs as \
               `mcp serve --transport http`, and a token from it comes before `apiKeyHelper` and \
               the stored login, as `CCR_OAUTH_TOKEN_FILE`. That file is no route on Windows: a \
               path with no drive names a folder at a drive's root, which every account on the \
               PC shares, so a token there would sign in every Claude Code run on that drive. \
               How Bun resolves the path there is not measured. Bun 1.4.3 calls a pipe or a \
               socket handed as descriptor 3 through `STARTUPINFOW.lpReserved2` a FIFO, and \
               opening `/proc/self/fd/3` fails with ENOENT: RF-WW1, runs 38797729796 and \
               38821312770",
        read_from: "the descriptor reader, its socket check and its well-known file read, and \
                    the order of the token sources it names, in the win32-x64 and win32-arm64 \
                    builds of 2.1.301, which embed Bun 1.4.3, read as bytes on 2026-10-16. The \
                    path is fixed when a build is made: the macOS build of 2.1.296 opens \
                    `/dev/fd/<n>`, which takes a pipe",
        verified_against: "2.1.301",
        depends: "warm::unbuilt, which warms up no account on Windows, and doctor's warm_up check",
        probe: &[
            "descriptor_by_path",
            "file_after_socket",
            "file_after_failed_read",
            "/home/claude/.claude/remote",
        ],
        absent: &[],
    },
    Assumption {
        name: "windows_statusline_shell",
        fact: "on Windows Claude Code runs a `statusLine` command as it runs a hook that names no \
               shell: with `bash -c` in Git Bash, the bash's folder put first on the command's \
               `PATH`, where it finds one, and otherwise in PowerShell, `-NoProfile \
               -NonInteractive -ExecutionPolicy Bypass -Command`, saying once a session that a \
               hook ran in PowerShell. Each starts hidden. The command is `statusLine.command` of \
               its settings. It finds Git Bash at `CLAUDE_CODE_GIT_BASH_PATH` where that names a \
               file there whose name, in lower case, is `bash.exe`, `sh.exe`, `bash` or `sh`, then at \
               `C:\\Program Files\\Git\\bin\\bash.exe`, then at \
               `C:\\Program Files (x86)\\Git\\bin\\bash.exe`, then at `..\\..\\bin\\bash.exe` from \
               the first `git` that `where.exe` finds outside the working folder. It hands bash \
               the command as written, backslashes and all, so the bare `pitboard statusline` \
               runs the same in either shell",
        read_from: "the Git Bash finder, the hook executor the status line's run goes through, \
                    its default shell, the notice it posts falling back to PowerShell, and the \
                    settings' `statusLine` and the runner that reads its `command`, in the \
                    win32-x64 and win32-arm64 builds of 2.1.301, read as bytes on 2026-10-16",
        verified_against: "2.1.301",
        depends: "doctor's status_line check, which says on Windows which shell runs the status \
                  line and whether a `pitboard` is on the PATH it is handed, and warns where the \
                  settings Pitboard reads run a bare `pitboard` and none is there",
        probe: &[
            r#"["bash.exe","sh.exe","bash","sh"]"#,
            ".CLAUDE_CODE_GIT_BASH_PATH).toLowerCase()",
            r#""C:\\Program Files\\Git\\bin\\bash.exe","C:\\Program Files (x86)\\Git\\bin\\bash.exe""#,
            r#""..","..","bin","bash.exe""#,
            "ran in PowerShell. Commands written for bash can fail there.",
            ".statusLine?.command",
        ],
        absent: &[],
    },
    Assumption {
        name: "windows_statusline_powershell",
        fact: "where it finds no Git Bash, Claude Code runs a status line in `pwsh` on `PATH`, \
               then `pwsh.exe` in `%ProgramFiles%\\PowerShell\\7`, \
               `%LOCALAPPDATA%\\Microsoft\\WindowsApps`, there only as an app execution alias \
               that leads to a file, or `%USERPROFILE%\\.dotnet\\tools`, then `powershell` on \
               `PATH`, then `%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`, \
               `SystemRoot` taken as `C:\\Windows` where unset. A program it finds named `pwsh` \
               is PowerShell 7, and any other Windows PowerShell",
        read_from: "the PowerShell finder the hook executor asks, and how it tells PowerShell 7 \
                    from Windows PowerShell, in the win32-x64 and win32-arm64 builds of 2.1.301, \
                    read as bytes on 2026-10-16, and in the win32-x64 build of 2.1.302 on \
                    2026-10-18. Measured on the session's PC, Windows 11 build 26300 on ARM64, \
                    on 2026-10-18 with 2.1.302: with neither Git nor PowerShell 7 installed it \
                    ran the line in powershell.exe, with PowerShell 7.6.7 in pwsh.exe, and with \
                    Git 2.55.0 in Git's bash.exe",
        verified_against: "2.1.302",
        depends: "claude::paths::powershell, by which doctor's status_line check names the \
                  PowerShell that runs the line, and the Windows tests that run `pitboard \
                  statusline` under Windows PowerShell 5.1 and under pwsh 7, either of which may \
                  be the one",
        probe: &[
            r#""PowerShell","7","pwsh.exe""#,
            r#""Microsoft","WindowsApps","pwsh.exe""#,
            r#"".dotnet","tools","pwsh.exe""#,
            r#"==="pwsh"?"core":"desktop""#,
            "fell_back_to_powershell_5",
        ],
        absent: &[],
    },
    Assumption {
        name: "proxy_from_variables",
        fact: "Claude Code sends its requests through the proxy the first of `https_proxy`, \
               `HTTPS_PROXY`, `http_proxy` and `HTTP_PROXY` that is set names, and exempts the \
               hosts `no_proxy` and `NO_PROXY` name, both where both are set. It reads no proxy \
               the system names: on Windows neither `Internet Settings` nor WinHTTP, so a proxy \
               only Windows' settings name carries none of its requests",
        read_from: "the proxy module's variable list, `getProxyUrlWithSource`, `getNoProxy` and \
                    `noProxyUnion`, in the darwin-arm64, linux-x64, win32-x64 and win32-arm64 \
                    builds of 2.1.301, read as bytes on 2026-10-16. None of the four holds \
                    `Internet Settings`, `ProxyEnable` or any `WinHttp` call",
        verified_against: "2.1.301",
        depends: "doctor's system_proxy note on Windows, which says Claude Code's requests go out \
                  directly where only Windows' settings name a proxy",
        probe: &[
            r#""https_proxy","HTTPS_PROXY","http_proxy","HTTP_PROXY""#,
            "Proxy settings must be a complete URL including the scheme",
        ],
        absent: &["Internet Settings", "WinHttpOpen"],
    },
    Assumption {
        name: "headless_token_descriptor",
        fact: "where `CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR` names a descriptor, Claude Code \
               reads an OAuth access token from it, an inherited pipe until a newline or its \
               end, and signs in with that token alone: no refresh token and no expiry, so it \
               is never renewed, and never written to the credential store. It is written to \
               disk only under `CLAUDE_CODE_REMOTE`. An ordinary sign-in's access token \
               serves, as a token from `claude setup-token` does: measured on 12 October 2026 \
               with the parked logins of two accounts. No keychain item was made",
        read_from: "the descriptor reader, the login it makes of the token it reads, and the \
                    disk handoff only a remote session makes; and measured in a private \
                    `CLAUDE_CONFIG_DIR` and `HOME`",
        verified_against: "2.1.296",
        depends: "Claude Code's warm-up command, which hands a run the access token alone, on \
                  descriptor 3, so a run can spend no refresh token a park or Claude Code \
                  still holds",
        probe: &[
            "CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR",
            "(until newline or end-of-stream)",
            "refreshToken:null,expiresAt:null",
        ],
        absent: &[],
    },
    Assumption {
        name: "headless_print_mode",
        fact: "`-p` answers one prompt and exits; `--model haiku` takes the smallest model; \
               `--tools \"\"` offers none; `--strict-mcp-config` starts no MCP server but \
               those named on the command line; `--disable-slash-commands` loads no skill; \
               `--no-session-persistence` keeps no session on disk; `--output-format json` \
               prints one JSON result. Print mode adds what it reads on standard input to the \
               prompt. Run so on 12 October 2026, a request cost 3,086 tokens written to the \
               cache and 6 others, and 7 to 15 seconds",
        read_from: "the command line's options and their help, and measured",
        verified_against: "2.1.296",
        depends: "Claude Code's warm-up command, its arguments, and the token kept off \
                  standard input",
        probe: &[
            "\"-p, --print\"",
            "\"--tools <tools...>\"",
            "\"--no-session-persistence\"",
            "\"--strict-mcp-config\"",
            "\"--disable-slash-commands\"",
            "\"--output-format <format>\"",
        ],
        absent: &[],
    },
    Assumption {
        name: "headless_result",
        fact: "print mode's JSON result holds `is_error`, `result`, the answer or what went \
               wrong in Claude Code's words, and where the service refused the request, \
               `api_error_status` and `api_error`: 429 with `usage_limit_reached` where a \
               limit of the account is used up, and exit status 1",
        read_from: "the result print mode prints, measured on 12 October 2026 on an account \
                    with its weekly limit used up and on two with room",
        verified_against: "2.1.296",
        depends: "Claude Code's warm-up verdict: what a run came to",
        probe: &["api_error_status", "usage_limit_reached"],
        absent: &[],
    },
    Assumption {
        name: "request_starts_the_five_hour_period",
        fact: "an account's five-hour period starts with its first request after the last one \
               reset, print mode's on the smallest model included, and counts on the plan, not \
               the monthly credit. It starts at the start of the ten minutes the request fell \
               in: a request at 17:52 gave a reset at 22:50, one at 18:02 gave 23:00. Until \
               then the usage answer gives the limit's row, `session`, at 0% with no reset, \
               and `five_hour` with no reset",
        read_from: "measured on 12 October 2026 on a Team 5x and a Max 20x account: the usage \
                    answer before a request and 20 seconds after it",
        verified_against: "2.1.296",
        depends: "warm::decide, which reads a period not running as one with no reset or a \
                  passed one, and counts a warm-up the last reading did not see as a period \
                  running from the start of its ten minutes",
        probe: &[],
        absent: &[],
    },
    Assumption {
        name: "weekly_period_is_a_fixed_cycle",
        fact: "the weekly limit, and a model's weekly limit beside it, runs on a fixed cycle: \
               at its reset the next week starts at once, with no request, at 0%",
        read_from: "measured on 12 October 2026 on a Max 20x account whose weekly limit reset \
                    at 18:00: at 18:02, before any request, the answer gave the next week's \
                    reset at 0%",
        verified_against: "2.1.296",
        depends: "warm::decide, which starts the five-hour period alone",
        probe: &[],
        absent: &[],
    },
    Assumption {
        name: "used_up_limit_refuses_every_request",
        fact: "while a limit of the account's own, not a model's, is used up, the service \
               refuses a request, which spends nothing and starts no five-hour period. Not \
               measured with only a model's limit used up",
        read_from: "measured on 12 October 2026 on a Max 20x account with its weekly limit \
                    used up: the request was refused with `usage_limit_reached`, 0 tokens, and \
                    the answer after it still had no five-hour period running",
        verified_against: "2.1.296",
        depends: "warm::decide, which warms up no account with a limit used up",
        probe: &[],
        absent: &[],
    },
    Assumption {
        name: "refusals_are_final",
        fact: "Claude Code asks Anthropic again after a network failure, a timeout, a 5xx, a \
               408, a 409 or a 429, and takes any other 4xx as final. An error answer from \
               `api.anthropic.com` is `{\"type\":\"error\",\"error\":{\"type\",\"message\", \
               \"details\"},\"request_id\"}`, and Claude Code shows `error.message` where \
               `details.error_visibility` is `user_facing`",
        read_from: "the retry rule the OAuth requests share and the profile, validate and \
                    roles requests' error handling; the envelope measured on 16 October 2026 \
                    from a 401 and a 405 of `/api/oauth/profile`",
        verified_against: "2.1.301",
        depends: "Refusal::is_final, which makes a final 4xx a refusal that is not worth \
                  retrying and keeps its message and request id",
        probe: &["error_visibility", "user_facing"],
        absent: &[],
    },
    Assumption {
        name: "account_refusals",
        fact: "a 403 `permission_error` whose `details.error_code` is `verification_required`, \
               and a 401 or 403 whose message says `OAuth authentication is currently not \
               allowed for this organization`, refuse the account, not an expired token: \
               Claude Code shows the message and does not renew",
        read_from: "the verification check of the API key and API errors, and the \
                    organisation check of the API errors",
        verified_against: "2.1.301",
        depends: "the Claude provider's reading of a 401 or 403, which takes these two as \
                  refusals and every other as a token to renew",
        probe: &[
            "verification_required",
            "OAuth authentication is currently not allowed for this organization",
        ],
        absent: &[],
    },
    Assumption {
        name: "token_refusals",
        fact: "the token endpoint's error answer is `{\"error\", \"error_description\", \
               \"error_uri\"}`, `error` a string or `{type}`. A 400, 401 or 403 whose `error` is \
               `invalid_grant` or `access_denied` and whose description is `account_on_hold` is \
               an account on hold: Claude Code keeps the login and shows the `error_uri`, only \
               an https page on `portal.anthropic.com`, `claude.ai` or `platform.claude.com`. \
               A 400 or 401 `invalid_grant` otherwise is a refresh token spent for good. \
               Descriptions it knows: `Refresh token expired`, `Refresh token not found or \
               invalid`, `No organization associated with this token`, `No account \
               associated with this token`",
        read_from: "the refresh's error readers: the on-hold check, the dead-token check, the \
                    invalid-scope fallback and the descriptions its failure event names, and \
                    the link check of the on-hold notice",
        verified_against: "2.1.301",
        depends: "the Claude provider's renewal, which keeps a park of an account on hold and \
                  drops one only for `invalid_grant`",
        probe: &[
            "account_on_hold",
            "No organization associated with this token",
            "portal.anthropic.com",
        ],
        absent: &[],
    },
    Assumption {
        name: "validate_names_the_login",
        fact: "`POST /api/oauth/validate` with an access token as `Authorization: Bearer`, \
               `Content-Type: application/json` and no body answers `{\"valid\", \
               \"account_uuid\", \"organization_uuid\", \"scopes\"}` for a token with \
               `user:profile`, `user:office` or `user:ccr_inference`. Claude Code asks it where \
               an identity decides something: whether a login is of an organisation managed \
               settings require, and which account the browser bridge serves, over the \
               config's. Its sign-in identifies a login by the profile or else the token \
               answer, and signs in where the profile cannot be read",
        read_from: "the validate request and its retry list, the organisation pin's check, the \
                    browser bridge's account, and the sign-in's identity; the answer's keys \
                    measured on 16 October 2026 from a Max login",
        verified_against: "2.1.301",
        depends: "the Claude provider's identity, which every switch, read and sign-in asks, \
                  and which needs no profile for an account Pitboard knows",
        probe: &[
            "/api/oauth/validate",
            "resolves to a different claude.ai account than the persisted Claude Code login",
        ],
        absent: &[],
    },
    Assumption {
        name: "token_answer_names_the_login",
        fact: "the token endpoint's answer to a renewal carries `account.uuid`, \
               `account.email_address` and, where the login has one, `organization.uuid`, \
               which Claude Code keeps as the login's `tokenAccount`",
        read_from: "the refresh's answer reader",
        verified_against: "2.1.301",
        depends: "the Claude provider's renewal, whose answer names the login renewed where it \
                  names all three, so nothing is asked about it after",
        probe: &["account.email_address", "tokenAccount:"],
        absent: &[],
    },
];

#[cfg(test)]
mod tests {
    use super::*;
    use crate::assumptions::Platform;

    // Read there, the keychain facts would report the keychain gone, and no_keyring_off_macos a
    // keyring arrived.
    #[test]
    fn windows_reads_the_facts_its_build_was_read_for() {
        let read: Vec<&str> = PER_SYSTEM
            .iter()
            .filter(|line| matches!(line.on(Platform::Windows), Read(_)))
            .map(|line| line.name)
            .collect();
        assert_eq!(
            read,
            [
                "write_lock",
                "logout_skips_the_lock",
                "account_scoped_keys",
                "config_file_location",
                "settings_byte_order_mark",
                "oauth_client",
                "sign_in_output",
                "sign_in_takes_another_code",
                "fallback_file_pins_session_login",
                "login_is_one_organisation",
                "config_may_name_no_organisation",
                "usage_cache_stamp_is_the_configs",
                "status_reads_the_config_usage_the_token",
                "config_identity_is_the_last_writers",
                "status_line_input_names_no_account",
                "refresh_lock",
                "login_carries_its_plan",
                "windows_home_folder",
                "windows_open_sharing",
                "windows_install_layout",
                "windows_version_resource",
                "windows_managed_settings",
                "windows_daemon_identity",
                "windows_backend_choice",
                "windows_plaintext_strict_read",
                "windows_plaintext_write",
                "windows_file_adoption",
                "credman_account_name",
                "credman_target",
                "credman_chunking",
                "credman_probe",
                "credman_migration",
                "credman_move_lost_a_piece",
                "credman_unreachable_writes_file",
                "credman_attributes",
                "windows_browser_open",
                "windows_token_handover",
                "windows_statusline_shell",
                "windows_statusline_powershell",
                "proxy_from_variables",
                "request_starts_the_five_hour_period",
                "weekly_period_is_a_fixed_cycle",
                "used_up_limit_refuses_every_request",
                "refusals_are_final",
                "account_refusals",
                "token_refusals",
                "validate_names_the_login",
                "token_answer_names_the_login",
            ]
        );
        for name in [
            "keychain_write_route",
            "keychain_absence_codes",
            "no_keyring_off_macos",
            "headless_token_descriptor",
            "headless_print_mode",
            "headless_result",
        ] {
            let line = PER_SYSTEM.iter().find(|l| l.name == name).unwrap();
            assert!(matches!(line.on(Platform::Windows), NotRead(_)), "{name}");
        }
    }
}