You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TestLoginUsageClaudeAskedMeanwhile flakes on Windows (3 of 5 on main) #1228
TestLoginUsageClaudeAskedMeanwhile in internal/provider fails on and off on a real Windows 11 machine. It failed 3 of 5 runs on main at fae8b8b, and the same way at #1226's head. It passes on macOS.
Found while reviewing #1226. Its cause isn't known yet; a likely suspect is timing in the test's wait for the in-flight usage read on Windows' coarser timer. To reproduce: cross-compile go test -c -tags nogui ./internal/provider for windows/amd64, then run -test.run TestLoginUsageClaudeAskedMeanwhile -test.count 5 with USERPROFILE/APPDATA/LOCALAPPDATA/TEMP pointed into a sandbox folder.
go test -tags nogui -count=50 -timeout=120s -run '^TestLoginUsageClaudeAskedMeanwhile$' ./internal/provider
原产品代码真实失败:
--- FAIL: TestLoginUsageClaudeAskedMeanwhile (0.19s)
claude_usage_fallback_test.go:263: a reading from before the ask was kept
FAIL
FAIL github.com/yetone/magpie/internal/provider 11.434s
临时诊断在缓存发布边界记录到真实时间相等(诊断已从最终源码移除):
DIAGNOSTIC equal wall-clock tick: asked = 1791790183943949300 start = 1791790183943949300
--- FAIL: TestLoginUsageClaudeAskedMeanwhile (0.15s)
claude_usage_fallback_test.go:263: a reading from before the ask was kept
git stash push -- internal/provider/login_usage.go internal/provider/subscription_usage.go internal/provider/quota_refresh.go 后原测试 -count=30,随后 pop
真实 FAIL,7.077s;恢复修法
pop 后原测试 -count=5 -timeout=90s
PASS,2.137s
主线程独立沙盒复核,原测试 -count=5 -timeout=90s
PASS,2.476s;最终提交未再改产品代码
go test -tags nogui -race -count=5 -timeout=240s -run E ./internal/provider,最终版本
PASS,122.691s
早先相关 usage 子集 -count=3
PASS,51.536s;不是最终全包测试
Windows go vet -tags nogui ./internal/provider,最终版本
EXIT 0
GOOS=linux go vet -tags nogui ./internal/provider,最终版本
EXIT 0
GOOS=darwin go vet -tags nogui ./internal/provider,最终版本
EXIT 0
Windows 默认 go build -o <output> .,最终版本
EXIT 0
GOOS=linux go build -tags nogui -o <output> .,最终版本
EXIT 0
GOOS=darwin go build -tags nogui -o <output> .,最终版本
EXIT 0;不是 Darwin GUI build
gofmt -l 三个修改的 Go 文件
0 输出
staged diff --check / name-status
无错误,仅三个产品文件和 owning 设计页
独立 format-patch 的 reverse apply check
EXIT 0
**未跑:**provider 全包、全量 go test -tags nogui ./...、全仓 vet、真实 Claude account、GUI 双引擎五语言、Darwin GUI build、Linux/Darwin 实机运行。此前整包/全量仍有其他未解决红项;本次没有靠定向绿声称全量验收通过。不请求直接关闭 #1228,合入前仍需维护者整合后的完整验收。
Generated-by: Factory Droid (GPT-6.1 Sol)
完整 standalone git format-patch
From c56eccab7ed38fee60b5d1c53db2c45a5aff86b9 Mon Sep 17 00:00:00 2001
From: ggbdpq <ggbdpq@gmail.com>
Date: Mon, 12 Oct 2026 16:29:02 +0800
Subject: [PATCH] provider: a Claude usage ask invalidates an older read even
in the same clock tick
Refs #1228. The original test fails with "a reading from before the ask
was kept". A temporary diagnostic measured identical UnixNano values for
the read's start and the later ask; that diagnostic is not shipped.
What changed:
- Count both whole-page and manual-card Claude asks with a generation.
Advance it under subscription then login cache locks, so publication
cannot check the old generation and then commit after the ask.
- Capture the generation before launching LoginUsage/SubscriptionUsage;
reject an older account read or leave the cards due for another read.
- Record each Claude /usage run's ask generation. Keep timestamps, the
zero sentinel, floor, idle behavior, saved-account scope and fallbacks.
- Document ordering and lock ownership in the providers/accounts design.
No tests, routing.go, account files or dependency versions changed.
Run on upstream 8f2a314cf9779151b9caa5d60183e594239d0621:
- Original test unchanged: count=50 passes (12.317s). Product-only stash
restores real red, count=30 (7.077s); pop then count=5 passes (2.137s).
- Expanded usage/header/saved-card paths race count=5 pass (122.691s),
Go 1.26.8 and installed MinGW gcc, external sandbox home/temp and
pinned Go caches.
- Provider nogui vet passes on Windows, Linux, Darwin. Windows default
build and Linux/Darwin nogui builds pass. gofmt and staged diff clean.
- Provider full package and full ./... were not rerun; real account,
GUI engines/languages, Darwin GUI build and cross-OS runtime not tried.
Targeted evidence only, not a claim of complete merge acceptance.
Generated-by: Factory Droid (GPT-6.1 Sol)
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
---
docs/subsystems/providers-accounts.md | 1 +
internal/provider/login_usage.go | 4 ++--
internal/provider/quota_refresh.go | 2 +-
internal/provider/subscription_usage.go | 30 ++++++++++++++++++++-----
4 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/docs/subsystems/providers-accounts.md b/docs/subsystems/providers-accounts.md
index ba463dbf..10f733ad 100644
--- a/docs/subsystems/providers-accounts.md+++ b/docs/subsystems/providers-accounts.md@@ -110,6 +110,7 @@ Account discovery can be turned off (#1535): Settings → Privacy → *Subscript
- Each refresh token has exactly one holder. Vendors rotate tokens on refresh, so two copies of one token would sign each other out. `savedTokenMu` stops two requests refreshing one saved account at once.
- A Claude subscription account is used only for Claude Code, through the genuine Claude Code binary (see [Claude subscription bridge](claude-subscription-bridge.md)).
+- Claude usage asks are ordered by `claudeAskGeneration`, not wall-clock timestamps. `AskClaudeUsage` and a Claude card's `RefreshUsage` advance it while holding the subscription cache then the login cache lock (`markClaudeUsageAsked`). Each cache captures it under its own lock before launching a read and checks it under that lock when publishing: a newer ask prevents `LoginUsage` from keeping the older reading and leaves `SubscriptionUsage` due to read again. Each `claudeWindows` entry records the generation its run answered. The timestamp's zero sentinel, `tried`/`wait`, the 30-second floor, idle behavior and saved-account scope remain unchanged. [`login_usage.go`](../../internal/provider/login_usage.go), [`subscription_usage.go`](../../internal/provider/subscription_usage.go), [`quota_refresh.go`](../../internal/provider/quota_refresh.go).
- A saved Claude account's allowance is read only when its own card is refreshed (#1630; `savedRefresh`: `RefreshUsage` for that account, so `cardRefresh.wants` it and the read is asked). The real Claude Code runs `/usage` with `CLAUDE_CONFIG_DIR` set to the account's `claudeSavedDir` (`readSavedClaudeUsage`, through the gateway's `claudeUsage`), so magpie never calls Anthropic with the token itself and the account Claude Code is signed in to is not switched. The Usage page opened, `magpie quota` and `magpie accounts` ask every card and so start no saved account's Claude Code: they keep its last reading, or `errClaudeSaved` before one. Background reads keep the last reading, and the 30-second per-account floor holds for repeated refreshes. Claude Code may refresh the account's token in that directory while it runs (its own lock file keeps two Claude Codes in one directory from refreshing at once). So `inSavedClaudeDir` holds `claudeDirRun(user)` for reading while it runs, which `switchSavedLogin` takes for writing first, and after the run `syncSavedClaude` copies a rotated sign-in back into `logins.json` under `savedTokenMu` and `loginsMu`. A saved account's warm-up runs the same way. [`claude_usage_saved_test.go`](../../internal/provider/claude_usage_saved_test.go) covers the refresh, the other accounts left alone, the floor, background reads, a switch waiting for the run and the rotated sign-in kept; `TestClaudeUsageRunsInSavedAccountDirectory` checks the directory the command runs in.
- Claude Code's `/usage` prints a reset with no year when it falls this year ("Oct 9, 2:59pm (UTC)"), or, in older versions, as a time alone ("3pm"). `claudeResetTime` reads it as the end of its window, so never further ahead than the window's length (5 hours for the session, 7 days for a week) and `claudeResetSlack`: a date is in the first of last year, this year and next that is less than a day past and no further ahead, a time alone the next one no further ahead. Where none is, as for a reading days old, it is the latest one already past, so the window reads as renewed rather than full for a year. Where the clocks go back and show a time twice, the reading not yet past is taken. A week's time alone read just after its reset still reads as tomorrow's.
- A Copilot account is known by its GitHub login and host (`CopilotAccountName`, #1220): `mona` on github.com, `mona@acme.ghe.com` on an enterprise's GHE.com. That name is its `logins.json` user, its row in every list, its usage card and its per-account routing settings, and it is what `SwitchLogin`, `SetLoginOn` and `ForgetLogin` take, so one login on two hosts is two accounts, neither written over by the other nor dropped as the editors' own. One saved before on an enterprise's host under its login alone is read under its name with the host (`copilotSavedName`), keeping its id; the editors' own remembered so is renamed in place, still hidden if it was (`copilotRenamed`). Either rename takes the account's per-account settings in `providers.json` (proxy, models, cap, concurrency) from the bare login to the new name before the new name is written to `logins.json` (`renameSettled`): a setting the new name has already stays, and while `providers.json` can't be read or written the account stays saved under its old name, its settings with it. `magpie accounts switch|forget copilot` takes the full name, the login with `--host <name>.ghe.com`, or the login alone when it names one account.
diff --git a/internal/provider/login_usage.go b/internal/provider/login_usage.go
index 2ecee8f2..9465e35d 100644
--- a/internal/provider/login_usage.go+++ b/internal/provider/login_usage.go@@ -198,8 +198,8 @@ func loginReading(ctx context.Context, l Login) loginUsageEntry {
}
c.pending[key] = r
ctx, _ = quotaReading(ctx)
+ asked := claudeAskGeneration.Load()
go func() {
- start := time.Now()
// read for all who wait for it: no one's ctx cuts it short, but
// it is bounded as the Usage page's refresh is
rctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), subscriptionTimeout)
@@ -211,7 +211,7 @@ func loginReading(ctx context.Context, l Login) loginUsageEntry {
// (AskClaudeUsage), as SubscriptionUsage reads it: neither is kept
if c.pending[key] == r {
delete(c.pending, key)
- if l.Agent != "claude" || claudeAsked.Load() <= start.UnixNano() {+ if l.Agent != "claude" || claudeAskGeneration.Load() == asked {
if c.m == nil {
c.m = map[string]loginUsageEntry{}
}
diff --git a/internal/provider/quota_refresh.go b/internal/provider/quota_refresh.go
index da55ef84..413a834d 100644
--- a/internal/provider/quota_refresh.go+++ b/internal/provider/quota_refresh.go@@ -104,7 +104,7 @@ func RefreshUsage(ctx context.Context, provider, user string) {
ctx, seq := quotaReading(ctx)
if provider == "claude" {
// Claude Code's /usage, run when the user asks (claudeWindows)
- claudeAsked.Store(time.Now().UnixNano())+ markClaudeUsageAsked()
}
var wg sync.WaitGroup
wg.Add(3)
diff --git a/internal/provider/subscription_usage.go b/internal/provider/subscription_usage.go
index edb2c986..3fcc8807 100644
--- a/internal/provider/subscription_usage.go+++ b/internal/provider/subscription_usage.go@@ -222,15 +222,15 @@ func SubscriptionUsage(ctx context.Context) []SubscriptionQuota {
bg = Asked(bg)
}
readCtx, seq := quotaReading(bg)
+ asked := claudeAskGeneration.Load()
go func() {
- start := time.Now()
out := fetchSubscriptionUsage(readCtx)
noteDailyCredits(out, time.Now())
noteQuotaHistory(out, time.Now())
c.Lock()
c.data = cacheCards(c.data, out, seq, false)
c.at, c.pending = time.Now(), nil
- if claudeAsked.Load() > start.UnixNano() {+ if claudeAskGeneration.Load() != asked {
c.at = time.Time{} // asked meanwhile: read again
}
c.Unlock()
@@ -616,6 +616,7 @@ type claudeUsageEntry struct {
ws []QuotaWindow
heard time.Time // when Claude Code last told it, answering
tried time.Time // when /usage was last run, answered or not
+ asked uint64 // the ask generation that run answered
wait time.Duration // how long after tried it runs again unasked
err error // what the last run said, when it failed
whole bool // /usage was read: ws has every window the account has
@@ -677,12 +678,28 @@ var claudeUsedSince = func(t time.Time) bool {
// nanoseconds; zero: never).
var claudeAsked atomic.Int64
+// claudeAskGeneration orders asks against readings, even when both happen+// in one wall-clock tick. tried and wait still schedule the next /usage.+var claudeAskGeneration atomic.Uint64++func markClaudeUsageAsked() {+ // Serialize the ask with both caches' snapshots and publications:+ // neither may observe the old generation, then publish after this ask.+ // When both locks are needed, the subscription cache comes first.+ subscriptionUsageCache.Lock()+ loginUsageCache.Lock()+ claudeAsked.Store(time.Now().UnixNano())+ claudeAskGeneration.Add(1)+ loginUsageCache.Unlock()+ subscriptionUsageCache.Unlock()+}+
// AskClaudeUsage is the user asking to see Claude's usage — opening the
// Usage page, refreshing it, `magpie quota` — so Claude Code's /usage is
// run at once rather than when the next unasked reading is due
// (claudeUsageWait); the next SubscriptionUsage waits for it.
func AskClaudeUsage() {
- claudeAsked.Store(time.Now().UnixNano())+ markClaudeUsageAsked()
AskUsage()
l := &loginUsageCache
l.Lock()
@@ -727,11 +744,11 @@ func claudeWindows(ctx context.Context, user string, active bool) ([]QuotaWindow
key := strings.ToLower(user)
c := &claudeUsage
now := time.Now()
- asked := claudeAsked.Load()+ asked := claudeAskGeneration.Load()
c.Lock()
e, ok := c.m[key]
c.Unlock()
- due := e.tried.IsZero() || asked > e.tried.UnixNano() ||+ due := e.tried.IsZero() || claudeAsked.Load() != 0 && asked > e.asked ||
active && now.Sub(e.tried) >= e.wait && (e.heard.After(e.tried) || claudeUsedSince(e.tried))
// nor, with allowances read only when asked, one nobody asked for
read := (active || savedRefresh(ctx, user)) && due && now.Sub(e.tried) >= claudeAskFloor && !holding(ctx)
@@ -746,6 +763,7 @@ func claudeWindows(ctx context.Context, user string, active bool) ([]QuotaWindow
c.m = map[string]claudeUsageEntry{}
}
e.tried, e.wait = now, claudeUsageWait()
+ e.asked = asked
c.m[key] = e
}
c.Unlock()
@@ -809,7 +827,7 @@ func claudeWindows(ctx context.Context, user string, active bool) ([]QuotaWindow
if c.m == nil {
c.m = map[string]claudeUsageEntry{}
}
- c.m[key] = claudeUsageEntry{at: now, ws: ws, heard: e.heard, tried: now, wait: e.wait, whole: true}+ c.m[key] = claudeUsageEntry{at: now, ws: ws, heard: e.heard, tried: now, asked: asked, wait: e.wait, whole: true}
c.Unlock()
// as of now, as a kept reading is: /usage gives a reset to the minute
// and can lag it, so a window it still counts full may have started
--
2.55.0.windows.4
A trace of the cause from @TryWorld2026, in #1071 and #1078 (not yet checked against the current test): loginReading registers its pending read and starts the goroutine that reads, and that goroutine takes start := time.Now() when it is first scheduled, not when it is started. When it is scheduled after the test's second AskClaudeUsage(), its start lands after the ask, so the guard keeps it as fresh, as the rule says. The <-running the test waits on is signalled by SubscriptionUsage's own Claude read, not by this goroutine, so the test reaches its assertion ("nothing is cached at all") without waiting for it. They saw it fail 2 of 20 under -race -count=20 on a Windows box at f1821f4, with no race reported. If that holds, the fix is to take the start time before the goroutine is started, or to make the test wait for loginReading's read too.
Correction to the comment above: on main the guard no longer compares a start time. 44b5b2b (10-12) takes claudeAskGeneration before the read's goroutine is started and keeps the reading only when no ask came in between, which removes the scheduling window that trace describes. This needs a fresh Windows run at current main before it is closed.
Activity
agent: Ante keeps the user's own keys, reads ANTE_HOME, sends no effo…
支持 Ante:把 magpie 作为 provider 写进 ~/.ante/catalog.json (#1210)
accounts: every subscription whose sign-in risk the window confirms i…
ggbdpq commentedon Oct 12, 2026
补充 #1228:提供可复红的修法及完整 standalone patch,不另开重复 issue,也不请求跳过全量验收直接合入。
提交:
c56eccab7ed38fee60b5d1c53db2c45a5aff86b9Fork 分支:fix/claude-usage-refresh-generation
下载完整 patch;正文末尾也附 patch 全文,复现和审阅不依赖本机文件。
合入时请保留原作者(git am/cherry-pick),或加
Co-authored-by: ggbdpq <ggbdpq@gmail.com>。1. 现象
原有
TestLoginUsageClaudeAskedMeanwhile的断言是对的:用户在 Claude /usage 读取进行中再次 ask,旧读取不应被保留为一分钟的缓存。Windows 上较粗的时钟更容易暴露失败,但出错的是通用生产代码的事件排序,不是 Windows 分支,也不需要给测试加 sleep 或放宽断言。本修复只处理 Claude usage 的三个缓存层及两个 ask 入口,不含 #1491 的 routing/usedCache reset 修法。测试、routing.go、账号文件及依赖版本没有修改。
2. 最小复现
原测试不改,Go 1.26.8,在独立且无 Git 祖先的临时 HOME/USERPROFILE/TEMP/TMP 下执行:
原产品代码真实失败:
临时诊断在缓存发布边界记录到真实时间相等(诊断已从最终源码移除):
running/release channel 已保证第二次 ask 晚于原读取开始,时钟却不能区分它们。对最新基线
8f2a314cf9779151b9caa5d60183e594239d0621加最终修法,原测试 count=50 通过;只 stash 三份产品文件后 count=30 再次得到同一真实 FAIL(7.077s),保留原测试与文档,再 pop 恢复修法,count=5 通过(2.137s)。3. 根因
loginReading在 goroutine 内记录 start,发布判断为claudeAsked <= start.UnixNano()。同 tick 的晚一次 ask 等于 start,旧读取被错误保留。SubscriptionUsage用claudeAsked > start.UnixNano()清零 cache age,同 tick 时漏掉该失效。claudeWindows用asked > entry.tried.UnixNano()判断新 ask,相同时间戳也不能表达不同次请求。AskClaudeUsage及 Claude card 的RefreshUsage,此前都只 Store 当前 UnixNano。时间戳适合 cache age、idle、floor 和窗口调度,不足以作为请求事件序号。历史 PR #1137 也记录过这一时钟原因,但没有交付本路径修法;#1093、#1092、#1078、#1071 仍将它列为已有 #1228 的红项。本次补充的增量是覆盖兄弟路径的 patch、产品-only 复红及最终回归证据,不宣称首次发现该根因。
最后复查时 #1228 仍 OPEN,无 assignee、无评论;最新 upstream 的三个产品文件没有吸收本修法,没有发现相关完整修复 PR 或维护者接手说明。
4. 修法与保留语义
新增
claudeAskGeneration,两个 ask 入口共享markClaudeUsageAsked:首次未读仍由
tried.IsZero()判定;已读且无新 ask 仍复用;claudeAsked的 timestamp 和 zero sentinel 保留。30 秒 per-account floor、tried/wait的 5–15 分钟调度、idle/used-since、窗口时间、错误 fallback、saved-account 只能由自己的 card refresh 读取等规则均保留。保存账号不会被切换,账号所属路径及 Windows 的配置路径没有改动。最终自审还补上 generation 更新与缓存发布的锁内串行化;原版仅靠原子 Load/Add,不能将“比较后写缓存”作为完整临界区。该补强与整套最终产品代码已经重新跑过下表的回归/race。
依照仓库 subsystem 文档规则,同一提交对
docs/subsystems/providers-accounts.md增加一条最小的顺序及锁所有权说明。最终提交仅三个产品文件及该设计页,28 insertions / 9 deletions;没有缓存、构建产物、测试或无关修改入库。5. 验证及未跑项
最终独立提交基于
8f2a314cf9779151b9caa5d60183e594239d0621。先前3d46d3a6分支/commit 保留,未重写它。所有 go 验证使用外部独立 E 盘 sandbox,设置 HOME、USERPROFILE、TEMP、TMP、APPDATA、LOCALAPPDATA、XDG_CONFIG/DATA/STATE_HOME;GOPATH、GOMODCACHE、GOCACHE 固定原有缓存,不读真实账号。race 使用已安装的 MinGW gcc、进程级 PATH/CC/CGO_ENABLED=1;cross vet/build 使用 CGO_ENABLED=0。没有修改全局环境、作者身份或依赖版本。
扩展 regex E:
-count=50 -timeout=120s-count=20-count=50 -timeout=120sgit stash push -- internal/provider/login_usage.go internal/provider/subscription_usage.go internal/provider/quota_refresh.go后原测试-count=30,随后 pop-count=5 -timeout=90s-count=5 -timeout=90sgo test -tags nogui -race -count=5 -timeout=240s -run E ./internal/provider,最终版本-count=3go vet -tags nogui ./internal/provider,最终版本GOOS=linux go vet -tags nogui ./internal/provider,最终版本GOOS=darwin go vet -tags nogui ./internal/provider,最终版本go build -o <output> .,最终版本GOOS=linux go build -tags nogui -o <output> .,最终版本GOOS=darwin go build -tags nogui -o <output> .,最终版本gofmt -l三个修改的 Go 文件**未跑:**provider 全包、全量
go test -tags nogui ./...、全仓 vet、真实 Claude account、GUI 双引擎五语言、Darwin GUI build、Linux/Darwin 实机运行。此前整包/全量仍有其他未解决红项;本次没有靠定向绿声称全量验收通过。不请求直接关闭 #1228,合入前仍需维护者整合后的完整验收。Generated-by: Factory Droid (GPT-6.1 Sol)
完整 standalone git format-patch
yetone commentedon Oct 12, 2026
@ggbdpq 回复晚了抱歉,补丁我们正在审阅。
main 上
internal/provider/login_usage.go:214仍然是claudeAsked.Load() <= start.UnixNano():同一个时钟刻度内的两次刷新会互相漏掉,你分支上的 c56ecca(fix/claude-usage-refresh-generation)改成按代次判断,是对的方向。合入时会保留你的作者身份。这个 issue 保持打开,合入后会贴出提交和版本。
provider: a Claude usage ask invalidates an older read even in the sa…
provider: tests that a Claude usage ask on a read's own clock tick is…
yetone commentedon Oct 12, 2026
@ggbdpq 感谢补丁!已合入 main,作者署名保留为你:
会随下一个版本发布。先不关 issue,等你确认新版本里正常再关。
yetone commentedon Oct 13, 2026
A trace of the cause from @TryWorld2026, in #1071 and #1078 (not yet checked against the current test):
loginReadingregisters its pending read and starts the goroutine that reads, and that goroutine takesstart := time.Now()when it is first scheduled, not when it is started. When it is scheduled after the test's secondAskClaudeUsage(), its start lands after the ask, so the guard keeps it as fresh, as the rule says. The<-runningthe test waits on is signalled bySubscriptionUsage's own Claude read, not by this goroutine, so the test reaches its assertion ("nothing is cached at all") without waiting for it. They saw it fail 2 of 20 under-race -count=20on a Windows box at f1821f4, with no race reported. If that holds, the fix is to take the start time before the goroutine is started, or to make the test wait forloginReading's read too.yetone commentedon Oct 13, 2026
Correction to the comment above: on main the guard no longer compares a start time. 44b5b2b (10-12) takes
claudeAskGenerationbefore the read's goroutine is started and keeps the reading only when no ask came in between, which removes the scheduling window that trace describes. This needs a fresh Windows run at current main before it is closed.ggbdpq commentedon Oct 13, 2026
已在当前 main(
e1b70c13,v0.1.1222-10-ge1b70c13)Windows 上确认(go 1.26.5,-tags nogui):TestLoginUsageClaudeAskedMeanwhile:-count=50全部通过TestLoginUsageClaudeAskedOnTheReadsTick/TestSubscriptionUsageClaudeAskedOnTheReadsTick/TestClaudeWindowsAskedOnTheRunsTick):各-count=20全部通过本机已装 Magpie 0.1.1220 也含修复。我这边没有别的问题,可以关单。
yetone commentedon Oct 13, 2026
@ggbdpq 感谢在 Windows 上的复测(e1b70c13 上
TestLoginUsageClaudeAskedMeanwhile-count=50 和三个同 tick 回归测试都通过),也再次感谢你的补丁(44b5b2b96)。按你的确认关闭这个 issue。