From 8432f395994369205ba7ad5a35218e8d8d1861dc Mon Sep 17 00:00:00 2001 From: AliceJump <149395013+AliceJump@users.noreply.github.com> Date: Thu, 8 Oct 2026 23:38:29 +0800 Subject: [PATCH] fix(review): display wait times in the local timezone --- .agents/skills/ok-script-pr-review/SKILL.md | 2 ++ .../coderabbit-review-helpers.ps1 | 6 +++++ .../test-coderabbit-helpers.ps1 | 12 ++++++++++ .../test-coderabbit-wait-mock.ps1 | 24 +++++++++++++++++-- .../wait-coderabbit-rate-limit.ps1 | 10 ++++++-- .../ok-script-pr-review/wait-coderabbit.ps1 | 2 +- .../wait-review-threads.ps1 | 1 + jetbrains | 2 +- 8 files changed, 53 insertions(+), 6 deletions(-) diff --git a/.agents/skills/ok-script-pr-review/SKILL.md b/.agents/skills/ok-script-pr-review/SKILL.md index 960382f4..ad75e7c7 100644 --- a/.agents/skills/ok-script-pr-review/SKILL.md +++ b/.agents/skills/ok-script-pr-review/SKILL.md @@ -145,6 +145,8 @@ description: 处理 ok-script-toolkit 主仓及 JetBrains 子仓的 PR 审阅意 `6` 仍需等待(`-Once` 不是超时)、`4` PR 关闭而停止等待、`11` head 变化且本轮数据丢弃、`2` 错误。 等待须在用户能从 Codex 侧边查看输出的终端会话后台运行,复用已有进程;不要默认隐藏 Start-Process,也不要为等待另建定时任务、heartbeat 或 automation,除非用户明确要求安排。 +终端中的等待截止时间和下一次额度查询时间跟随电脑当前时区,显示完整日期、UTC 偏移与剩余时长; +等待已停止时也显示建议的查询时间。它不是审阅预计完成时间。JSON 的 ISO 时间格式保持不变。 本机账本默认在 `%LOCALAPPDATA%\ok-script-pr-review\coderabbit-triggers`,按仓库、PR 与 head 记录 触发,写入先于发送;`-StateDir` 可改位置。修改脚本后运行 `test-coderabbit-helpers.ps1` 与 `test-coderabbit-wait-mock.ps1`、`test-review-threads-mock.ps1`(Windows PowerShell 5.1 与 PowerShell 7 都应通过)。含非 ASCII diff --git a/.agents/skills/ok-script-pr-review/coderabbit-review-helpers.ps1 b/.agents/skills/ok-script-pr-review/coderabbit-review-helpers.ps1 index de673237..a78d5d0d 100644 --- a/.agents/skills/ok-script-pr-review/coderabbit-review-helpers.ps1 +++ b/.agents/skills/ok-script-pr-review/coderabbit-review-helpers.ps1 @@ -15,6 +15,12 @@ function Get-CrNow { return [datetimeoffset]::UtcNow } +# Human-readable times follow the computer's current timezone; protocol timestamps stay in UTC. +function Format-CrDisplayTime { + param([datetimeoffset]$Time, [TimeZoneInfo]$TimeZone = [TimeZoneInfo]::Local) + return [TimeZoneInfo]::ConvertTime($Time, $TimeZone).ToString('yyyy-MM-dd HH:mm:ss zzz', [Globalization.CultureInfo]::InvariantCulture) +} + function Wait-CrSeconds { param([double]$Seconds) if ($Seconds -le 0) { return } diff --git a/.agents/skills/ok-script-pr-review/test-coderabbit-helpers.ps1 b/.agents/skills/ok-script-pr-review/test-coderabbit-helpers.ps1 index 72e9cff2..ef5792f1 100644 --- a/.agents/skills/ok-script-pr-review/test-coderabbit-helpers.ps1 +++ b/.agents/skills/ok-script-pr-review/test-coderabbit-helpers.ps1 @@ -17,6 +17,18 @@ function Assert-Equal { if ($Actual -ne $Expected) { throw "$Message -> got '$Actual', expected '$Expected'" } } +# ---- display timezone: follow the host, including date rollover and fractional offsets ---- +$displayInstant = [datetimeoffset]'2026-10-08T18:04:05Z' +foreach ($case in @( + @{ Minutes = 0; Want = '2026-10-08 18:04:05 +00:00' }, + @{ Minutes = 480; Want = '2026-10-09 02:04:05 +08:00' }, + @{ Minutes = -300; Want = '2026-10-08 13:04:05 -05:00' }, + @{ Minutes = 330; Want = '2026-10-08 23:34:05 +05:30' })) { + $zone = [TimeZoneInfo]::CreateCustomTimeZone("test-$($case.Minutes)", [TimeSpan]::FromMinutes($case.Minutes), 'test', 'test') + Assert-Equal (Format-CrDisplayTime $displayInstant $zone) $case.Want 'display uses the requested timezone' +} +Assert-Equal (Format-CrDisplayTime $displayInstant) ($displayInstant.ToLocalTime().ToString('yyyy-MM-dd HH:mm:ss zzz', [Globalization.CultureInfo]::InvariantCulture)) 'default display follows host timezone' + # ---- quota replies: a countdown is unavailable; only an explicit positive reply is available ---- $plan = '' + "`n" + 'Your [plan](https://docs.coderabbit.ai/management/plans#fair-usage-limits-policy) includes PR reviews subject to [rate limits](https://docs.coderabbit.ai/management/plans#rate-limits).' diff --git a/.agents/skills/ok-script-pr-review/test-coderabbit-wait-mock.ps1 b/.agents/skills/ok-script-pr-review/test-coderabbit-wait-mock.ps1 index c5d2ef54..f72aa1ac 100644 --- a/.agents/skills/ok-script-pr-review/test-coderabbit-wait-mock.ps1 +++ b/.agents/skills/ok-script-pr-review/test-coderabbit-wait-mock.ps1 @@ -129,8 +129,8 @@ function Invoke-Wait([hashtable]$Extra = @{}) { function Invoke-Quota([hashtable]$Extra = @{}) { $params = @{ Repo = $repo; PrNumber = $pr; ExpectedHead = $global:W.head; PollSeconds = 10; ReplyTimeoutSeconds = 60 } foreach ($key in $Extra.Keys) { $params[$key] = $Extra[$key] } - $lines = @(& $quotaScript @params 6>$null) - return [pscustomobject]@{ exit = $LASTEXITCODE; result = ([string]$lines[-1] | ConvertFrom-Json) } + $lines = @(& $quotaScript @params 6>&1) + return [pscustomobject]@{ exit = $LASTEXITCODE; result = ([string]$lines[-1] | ConvertFrom-Json); log = ($lines | ForEach-Object { "$_" }) -join "`n" } } function Test-Case([string]$Name, [scriptblock]$Body) { try { & $Body; $script:passed++; Write-Output "ok $Name" } @@ -151,6 +151,26 @@ function Reply-OnProbe([string[]]$Replies) { } } +Test-Case 'quota wait displays local date, offset and time remaining' { + New-World + Reply-OnProbe @('More reviews will be available in 2 minutes.', 'Reviews are available now.') + $r = Invoke-Quota @{ MaxWaitSeconds = 300 } + $expected = $t0.AddSeconds(150).ToLocalTime().ToString('yyyy-MM-dd HH:mm:ss zzz', [Globalization.CultureInfo]::InvariantCulture) + Assert-True ($r.log.Contains("next query at $expected (local time; in 00:02:30)")) 'next query must use the local timezone with a countdown' + Assert-True ($r.log.Contains('review completion time is unknown')) 'do not present quota recheck as review completion' + Assert-True ($r.result.state -eq 'AVAILABLE' -and (Get-Now) -eq $t0.AddSeconds(150)) 'display formatting must not change retry scheduling' +} + +Test-Case 'stopped quota wait still displays suggested local retry time' { + New-World + Reply-OnProbe @('More reviews will be available in 2 minutes.') + $r = Invoke-Quota @{ MaxWaitSeconds = 60 } + $expected = $t0.AddSeconds(150).ToLocalTime().ToString('yyyy-MM-dd HH:mm:ss zzz', [Globalization.CultureInfo]::InvariantCulture) + Assert-True ($r.log.Contains("suggested next query at $expected (local time; in 00:02:30); this wait has stopped")) 'stopped wait must give a readable retry time' + Assert-True ($r.result.nextCheckAt -eq $t0.AddSeconds(150).ToString('o')) 'JSON timestamp must keep its protocol format' + Assert-True ($r.result.state -eq 'UNAVAILABLE' -and (Get-Now) -eq $t0) 'stopped wait must not keep sleeping' +} + Test-Case 'covered head ends REVIEWED without any write' { New-World Add-Status $headA 'success' 'Review completed' diff --git a/.agents/skills/ok-script-pr-review/wait-coderabbit-rate-limit.ps1 b/.agents/skills/ok-script-pr-review/wait-coderabbit-rate-limit.ps1 index 4fc237c6..388b56e6 100644 --- a/.agents/skills/ok-script-pr-review/wait-coderabbit-rate-limit.ps1 +++ b/.agents/skills/ok-script-pr-review/wait-coderabbit-rate-limit.ps1 @@ -53,6 +53,7 @@ function Wait-WithSessionCheck { try { $deadline = (Get-CrNow).AddSeconds($MaxWaitSeconds) + Write-Host "[rate-limit] waiting deadline: $(Format-CrDisplayTime $deadline) (local time; maximum wait $MaxWaitSeconds seconds; not a review completion estimate)" for ($probeCount = 1; $probeCount -le $MaxProbes; $probeCount++) { $stop = Test-Session if ($stop) { Complete-RateLimit $stop @{ actualHead = $script:ActualHead } } @@ -90,8 +91,13 @@ try { $delay = if ($null -ne $suggested) { $suggested + $RecheckBufferSeconds } else { $DefaultRecheckSeconds } $nextCheck = (Get-CrNow).AddSeconds([Math]::Max(1, $delay)) $detail = @{ probeId = $probe.id; replyId = $reply.comment.id; reply = Get-CrPlainText $reply.comment.body; nextCheckAt = $nextCheck.ToString('o') } - if ($nextCheck -gt $deadline -or $probeCount -eq $MaxProbes) { Complete-RateLimit 'UNAVAILABLE' $detail } - Write-Host "[rate-limit] unavailable; next query at $($nextCheck.ToString('u')) (a countdown is not proof of quota)" + $remaining = [TimeSpan]::FromSeconds([Math]::Max(0, [Math]::Ceiling(($nextCheck - (Get-CrNow)).TotalSeconds))) + $nextQuery = "$(Format-CrDisplayTime $nextCheck) (local time; in $($remaining.ToString('c')))" + if ($nextCheck -gt $deadline -or $probeCount -eq $MaxProbes) { + Write-Host "[rate-limit] unavailable; suggested next query at $nextQuery; this wait has stopped (review completion time is unknown)" + Complete-RateLimit 'UNAVAILABLE' $detail + } + Write-Host "[rate-limit] unavailable; next query at $nextQuery (a countdown is not proof of quota; review completion time is unknown)" $stop = Wait-WithSessionCheck $nextCheck if ($stop) { Complete-RateLimit $stop @{ actualHead = $script:ActualHead } } } diff --git a/.agents/skills/ok-script-pr-review/wait-coderabbit.ps1 b/.agents/skills/ok-script-pr-review/wait-coderabbit.ps1 index 14909d0a..d51e0f0e 100644 --- a/.agents/skills/ok-script-pr-review/wait-coderabbit.ps1 +++ b/.agents/skills/ok-script-pr-review/wait-coderabbit.ps1 @@ -128,7 +128,7 @@ try { $arrival = $null $unconfirmedSince = $null $restart = $false - Write-Host "[wait] session for $Repo#$PrNumber head=$head" + Write-Host "[wait] session for $Repo#$PrNumber head=$head; waiting deadline: $(Format-CrDisplayTime $globalDeadline) (local time; maximum wait $TimeoutSeconds seconds; not a review completion estimate)" while (-not $restart) { if ((Get-CrNow) -ge $globalDeadline) { Complete-Wait 'TIMEOUT' $head -Extra @{ detail = 'overall timeout' } } diff --git a/.agents/skills/ok-script-pr-review/wait-review-threads.ps1 b/.agents/skills/ok-script-pr-review/wait-review-threads.ps1 index f32c00f6..19865720 100644 --- a/.agents/skills/ok-script-pr-review/wait-review-threads.ps1 +++ b/.agents/skills/ok-script-pr-review/wait-review-threads.ps1 @@ -162,6 +162,7 @@ try { } $started = Get-CrNow $deadline = $started.AddSeconds($TimeoutSeconds) + if (-not $Once) { Write-Host "[threads] waiting deadline: $(Format-CrDisplayTime $deadline) (local time; maximum wait $TimeoutSeconds seconds; not a review completion estimate)" } $pr = Get-CrPr $Repo $PrNumber if (-not $ExpectedHead) { $ExpectedHead = $pr.head } $polls = 0 diff --git a/jetbrains b/jetbrains index 0fb95a07..852eca11 160000 --- a/jetbrains +++ b/jetbrains @@ -1 +1 @@ -Subproject commit 0fb95a0744e767dc2fb704e79eca7a03622bc375 +Subproject commit 852eca11ae25ab7444c3aa202be5b2d667759cf9