Skip to content

fix(components): subtract permissionWaitMs from Worked-for duration - #509

Merged
Leeeon233 merged 2 commits into
LodyAI:mainfrom
ladydd:fix/subtract-permission-wait-duration
Sep 9, 2026
Merged

Leeeon233 merged 2 commits into
LodyAI:mainfrom
ladydd:fix/subtract-permission-wait-duration

Conversation

@ladydd

@ladydd ladydd commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Author type

  • I am an Agent (check this if an LLM agent authored this PR)
  • I am a human

An AI agent found the mismatch and implemented the subtraction. A human reviewed the live OSS runs, the schema/renderer comparison, and this patch before submit.

Related issue

Closes #512

Distinct from closed #260 (that restamped endedAt after quit/reopen). This change is the live Worked-for label on a turn that never quit.

Problem / pressure

The Worked-for duration display includes permission wait time. Only turns that actually wait on a permission card are affected.

The schema already stores the wait and documents the formula (packages/shared/src/schema.ts):

Effective agent working time = (endedAt - Date.parse(timestamp)) - permissionWaitMs

CLI writes permissionWaitMs when a permission resolves. resolveSessionHistoryDurationMs only Picks endedAt and timestamp, so the number on screen is wall clock, including time spent at Implement this plan?.

Live OSS 0.76.0, Codex 5.6-Sol, Plan on, Auto review (so a second file-permission wait does not stack). After Yes, implementation was ~15s in each group.

Wait after the plan card Logged / persisted permissionWaitMs On-screen 工作了
Click Yes immediately 474ms 29秒
Wait 1 minute 62269ms 1分 27秒
Wait 10 minutes 601318ms 10分 28秒

Immediate Yes (control):

Worked for 29 seconds after clicking Yes immediately

Wait 1 minute, then Yes:

Worked for 1 minute 27 seconds after waiting one minute

Wait 10 minutes, then Yes:

Worked for 10 minutes 28 seconds after waiting ten minutes

Repro: OSS desktop, Codex, Plan on. Send a prompt that pops Implement this plan?. Do not quit. Wait, click Yes, let the file write finish. Worked-for tracks the wait, not the ~15s of work.

Summary

resolveSessionHistoryDurationMs now subtracts a finite permissionWaitMs and clamps a negative remainder to 0. Turns with no wait field stay endedAt - timestamp. endedAt is not restamped.

buildChatStreamItems copies permissionWaitMs onto the parsed message and invalidates the stream cache when the field changes, so the production chat path actually reaches the helper.

Before / after

Before After
Worked-for = endedAt - timestamp (wall clock, wait included) Worked-for = that span minus finite permissionWaitMs, clamped to 0
Immediate Yes, ~15s of work → 29秒 Unchanged order of magnitude (no meaningful wait)
Wait 1 / 10 minutes at the plan card → 1分27秒 / 10分28秒 Those waits subtracted; label should match remaining work

Test plan

On fix/subtract-permission-wait-duration after rebase onto origin/main 084ddb86:

corepack pnpm --filter @lody/components exec vitest run tests/session-history-duration.test.ts

Result: 3 passed (no wait unchanged; recorded wait subtracted; wait longer than the span → 0).

corepack pnpm --filter @lody/components exec vitest run tests/build-chat-stream-items.test.ts

Result: 15 passed, including copying permissionWaitMs onto the rendered message and not reusing a cached item when the wait changes.

Live OSS after the projection fix, same 1-minute wait recipe: 工作了 38秒, not 1分27秒. Screenshot in the review-thread reply.

Context handoff

Context handoff is public. An invalid fork PR body receives seven days to be corrected before closure.

Instructions for reviewing agents

Authoring context

  • User goal / directives: Make Worked-for match the schema formula so permission wait is not shown as agent work. Human reviewed the live runs and this patch.
  • Constraints / non-goals: Display math only. Do not restamp endedAt. Do not change permission UX, title overwrite, or dead Yes buttons.
  • Risk-bearing decisions: Read-only use of already-persisted permissionWaitMs. Missing or non-finite wait is treated as 0.
  • Destructive or irreversible behavior: None. No history rewrite, no migration, no rollback path required.
  • Deliberately not done or tested: Full components suite and Electron e2e; three unit tests plus the live OSS screenshots above.
  • Unknowns / confidence: High for the helper. Multi-permission stacked waits were excluded from the live table (Auto review) and are not claimed here.

The schema already records time spent waiting on permission and
documents effective work as (endedAt - timestamp) - permissionWaitMs.
The renderer only subtracted the two clocks, so leaving a plan card
for a minute or ten showed that wait as "Worked for …".
@github-actions github-actions Bot added scope: components status:needs-pr-attention External PR needs contributor attention before review labels Sep 8, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a05c7c1e58

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +20 to +21
const wait = message.permissionWaitMs;
if (typeof wait !== 'number' || !Number.isFinite(wait) || wait <= 0) return span;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Propagate the wait field into rendered messages

In the production chat flow, ai-gui/index.tsx passes history through buildChatStreamItems(), but its message projection in build-chat-stream-items.ts:151-168 omits permissionWaitMs (and its cache predicate does not track the field). Consequently message.permissionWaitMs is always undefined here, this function returns the original wall-clock span, and the shipped Worked-for bug remains; copy the field into the parsed message and include it in cache invalidation. The new test misses this because it calls the helper directly.

AGENTS.md reference: AGENTS.md:L79-L79

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed. Production chat goes ai-gui/index.tsx → buildChatStreamItems(). The projection at build-chat-stream-items.ts did not copy permissionWaitMs, and the cache predicate did not watch it, so the helper on this PR was a no-op on the real path. The original unit test called the helper directly, which is why it stayed green.

Pushed b62e3a07 on this branch: copy permissionWaitMs onto the parsed message; invalidate the stream cache when it changes. Tests in build-chat-stream-items.test.ts cover the copy and the cache miss, and run resolveSessionHistoryDurationMs on the projected message.

Live OSS after that commit, same 1-minute wait at Implement this plan?: 工作了 38秒, not 1分27秒.

After fix: Worked for 38 seconds following a one-minute wait

buildChatStreamItems omitted the field, so Worked-for still used wall
clock on the real render path. Copy it onto the parsed message and
invalidate the stream cache when it changes.
@github-actions github-actions Bot removed the status:needs-pr-attention External PR needs contributor attention before review label Sep 8, 2026
@Leeeon233

Copy link
Copy Markdown
Contributor

Thanks

@Leeeon233
Leeeon233 merged commit 4d6eaca into LodyAI:main Sep 9, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Worked-for duration includes time spent waiting on Implement this plan? [Bug] Completed turn duration grows after the app is quit and reopened

2 participants