Skip to content

fix(desktop): read Usage from Runtime Host - #2641

Open
me2seeks wants to merge 20 commits into
apache:mainfrom
me2seeks:fix/2128-host-backed-usage
Open

fix(desktop): read Usage from Runtime Host#2641
me2seeks wants to merge 20 commits into
apache:mainfrom
me2seeks:fix/2128-host-backed-usage

Conversation

@me2seeks

@me2seeks me2seeks commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • move Settings → Usage off the legacy session scanner and onto the reconnectable Runtime Host client
  • preserve missing token and pricing basis through the Host protocol, then show clean known totals with one coverage notice and precise request-row labels
  • keep aborted tool calls separate from successful calls and remove the legacy Storage read model

The Desktop adapter uses one explicit time window and retries when paged Host projections do not describe the same snapshot. Coverage stays page-level because legacy, unreadable, and pending records cannot be attributed to individual provider or model buckets without guessing.

Fixes #2128

Verification

  • npm run lint
  • npm run format:check
  • npx knip --workspace apps/desktop
  • npm --workspace @maka/core test (821 passed)
  • npm --workspace @maka/storage test (795 passed, 16 skipped)
  • npm --workspace @maka/runtime-host test (838 passed)
  • npm --workspace @maka/desktop test (1158 passed)
  • npm --workspace @maka/desktop run build:renderer
  • Storybook render smoke (129 stories), including incomplete, narrow, detailed, tool, and legacy Usage states

Screenshots

Request log

Usage request log with one coverage notice and precise missing, partial, and unpriced labels

Provider totals

Usage provider totals showing clean known subtotals with a page-level coverage notice

Tool totals

Usage tool totals showing aborted calls separately

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No
简体中文

摘要

  • 将“设置 → 使用统计”从旧的 Session 扫描器切换到可重连的 Runtime Host client
  • 在 Host 协议中保留 Token 与价格缺失的依据,以干净的已知数值配合一条覆盖情况提示展示,并在请求明细中准确区分未上报、部分数据、未定价和未记录
  • 将中断的工具调用与成功调用分开,并删除旧的 Storage 读模型

Desktop adapter 使用同一个明确时间窗口;如果分页读取到的 Host 投影不属于同一快照,则会重新读取,而不是拼接后返回。覆盖情况保持在页面级,因为历史、无法读取和待修复的记录不能在不猜测的情况下归到某个供应商或模型。

验证

  • Core、Storage、Runtime Host 与 Desktop 全量测试通过
  • lint、format、Desktop knip、typecheck 与 renderer build 通过
  • 129 个 Storybook 场景通过,包括不完整、窄屏、明细、工具和纯历史 Usage 状态

AI use

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Maka authored the existing Usage implementation, tests, verification, documentation, and visual evidence. OpenAI Codex authored the Runtime Host compatibility-epoch follow-up and the latest total-token provenance review remediation with its regression tests.

Final squash trailers:

Generated-by: Maka
Generated-by: Codex

@me2seeks
me2seeks marked this pull request as ready for review August 10, 2026 13:21
@me2seeks
me2seeks marked this pull request as draft August 10, 2026 13:27
@me2seeks
me2seeks marked this pull request as ready for review August 10, 2026 14:56
@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from e76722b to bc753ae Compare August 12, 2026 14:29

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Codex automated review — corrected after checking #2128 acceptance

I reviewed exact head ee488a7a44808af162200cdbf1862936435cd0fd across the Runtime Host usage authority, canonical/legacy ledger merge, Desktop multi-query projection, recovery markers, fixtures, and upgrade behavior.

Correction: I removed the original P1 about retaining Session-metadata-only usage after checking the linked #2128 acceptance criteria. That issue explicitly requires removing the legacy Session scanner and avoiding a second durable Usage authority, so treating those rows as a mandatory fallback would contradict the stated design.

One confirmed P2 remains: the five Desktop queries are not bound to a stable Host revision, so an upsert that changes tokens/cost/status without changing row count or coarse provenance can produce an internally inconsistent UsageStats; see the remaining inline finding.

A second P2 contract mismatch is visible in the same page: legacy telemetry stores totalTokens as input + output + reasoning, while canonical projection and each request row use input + output. The Host merge and Desktop projection add/show those totals directly, so reasoning-heavy legacy rows make summary/provider/model totals disagree with the request table. This should be normalized at the Usage authority (or the contract/labels must explicitly change), rather than patched independently in the renderer.

This is a cohesive usage-authority migration and I do not recommend splitting it. Current checks are green.

Disclosure: This is an automated review performed by Codex using delegated adversarial review passes and a final evidence check. It has not been independently verified by Astro-Han or another human reviewer, does not constitute human approval, and does not represent the final judgment of a human reviewer.

Comment thread apps/desktop/src/main/runtime-host-usage-ipc-main.ts Outdated
@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from ee488a7 to 539ca8e Compare August 13, 2026 05:08

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for closing the two original consistency issues. At 539ca8e2, Desktop now requires one Host revision across all five Usage views, and the current recorder no longer adds reasoning tokens twice.

After tracing the remaining failure paths, I think the authority cutover is still incomplete at one shared boundary: Usage reads have moved to Runtime Host, but protocol compatibility, repair ownership, test producers, and legacy token provenance have not all moved with them.

  1. The new required revision field changes the usage.query wire schema without advancing the compatibility epoch. A new Desktop can therefore accept an older remote Host during the handshake and fail only when it decodes the first old-format Usage response. After rebasing onto current main, the epoch should advance beyond 20 so incompatibility is rejected before domain requests are admitted.

  2. A Usage query currently mutates the authority inside its own revision fence. readCanonicalUsage() repairs pending projections, while each record and clear advances the same Usage revision that the query compares before and after reading. More than 32 pending runs exhaust the three retries; a single pending run containing both a valid attempt and an unreadable event can fail indefinitely because its marker remains and the valid attempt is recorded again on every retry. In both cases the query returns internal_failure instead of the qualified result that pendingRepairs and unreadableRecords were designed to represent.

    The smallest complete fix is to run at most one bounded repair pass before entering the fenced, read-only projection. The cleaner final state would give repair its own lifecycle owner and make idempotent writes advance the revision only when stored data actually changes.

  3. The settings-usage fixture still writes only transcript token_usage and tool messages. Once the old scanner is deleted, those records are no longer inputs to the production Usage path, so the fixture seeds empty Host Usage stores and no longer tests the page it claims to cover. It should seed the Host-owned telemetry/model-call seam, including missing usage and an aborted tool, rather than restoring or emulating the deleted scanner.

  4. The legacy total-token normalization still infers provenance from whether rawUsage.total_tokens exists. That cannot distinguish an old derived fallback from an explicitly supplied camel-case totalTokens. For example, an explicit input=20, output=8, reasoning=2, totalTokens=30 without raw snake-case usage is silently rewritten to 28. Please define the invariant at the persistence boundary—either total is always input + output, or reported versus derived total carries explicit provenance—rather than guessing it during reads.

These are different symptoms of the same incomplete cutover. I would keep the old scanner deleted and finish the migration at the natural owners instead of adding renderer retries, fallback reads, or compatibility paths. The PR remains one cohesive Usage-authority change and does not need to be split, but I would hold the merge until these boundaries are closed and the rebased CI is green.

简体中文

感谢修复最初的两处一致性问题。在 539ca8e2 上,Desktop 已要求五个 Usage 视图使用同一个 Host revision,当前 recorder 也不再把 reasoning token 重复计入 total。

继续追踪剩余失败路径后,我认为这次 authority cutover 仍缺少最后一层闭环:Usage 读取已经迁移到 Runtime Host,但协议兼容、repair owner、测试数据生产者和历史 token provenance 还没有全部随之迁移。

  1. 新增必需的 revision 字段改变了 usage.query wire schema,却没有提升 compatibility epoch。新版 Desktop 因此可能在握手时接受旧版远程 Host,直到解析第一条旧格式 Usage 响应时才失败。rebase 到当前 main 后,应将 epoch 从当前的 20 继续提升,使不兼容在接收领域请求之前就被拒绝。

  2. Usage 查询目前会在自己的 revision fence 内修改同一 authority。readCanonicalUsage() 会修复 pending projection,而每次 recordclear 都会推进查询前后正在比较的 Usage revision。超过 32 个 pending run 时会耗尽三次重试;如果一个 pending run 同时包含合法 attempt 和不可解码事件,其 marker 会保留,合法 attempt 又会在每次重试中重复写入,从而可能永久失败。两种情况最终都返回 internal_failure,而不是 pendingRepairsunreadableRecords 原本用于表达的不完整但可用结果。

    最小完整修复是在进入只读 revision fence 之前最多执行一次 bounded repair。更干净的终态是让 repair 拥有独立生命周期,并让幂等写入只在存储内容真正变化时推进 revision。

  3. settings-usage fixture 仍然只写 transcript 中的 token_usage 和 tool messages。旧 scanner 删除后,这些记录已不再是生产 Usage 路径的输入,因此 fixture 实际留下的是空的 Host Usage stores,已经无法覆盖它声称测试的页面。应通过 Host-owned telemetry/model-call seam 写入数据,并包含 missing usage 和 aborted tool,而不是恢复或模拟已经删除的 scanner。

  4. legacy total-token normalization 仍通过 rawUsage.total_tokens 是否存在来推断来源。它无法区分旧的 derived fallback 和显式传入的 camel-case totalTokens。例如显式的 input=20output=8reasoning=2totalTokens=30,在没有 raw snake-case usage 时会被静默改写为 28。这里应在持久化边界明确合同:要么 total 始终等于 input + output,要么为 reported/derived total 保存明确 provenance,而不是在读取时猜测。

这些是同一次 cutover 尚未闭环的不同表现。我建议继续删除旧 scanner,并在自然 owner 上完成迁移,不要增加 renderer 重试、fallback read 或兼容旧路径。这个 PR 仍是一个完整、内聚的 Usage authority 变更,不需要拆分;但上述边界闭合并且 rebase 后 CI 重新通过之前,我建议先不要合并。

Disclosure: I used Codex to assist with source tracing and adversarial review. I checked the cited production paths and failure conditions and take responsibility for this review.

@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from 539ca8e to 9fe90f2 Compare August 17, 2026 14:51
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Problem solved

Desktop Settings → Usage now reads from the reconnectable Runtime Host client instead of the legacy session scanner.

The change preserves missing token data, unknown costs, aborted tool-call status, custom pricing, consistent time ranges, and snapshot consistency across summaries, aggregates, and request rows. It also adds coverage notices and removes the legacy usage authority.

Source of truth

The PR extends the existing Host-owned canonical usage, telemetry, and pricing stores. It does not create a parallel Usage authority.

It removes the legacy scanner, readUsageStats, SettingsStore.usageStats, direct legacy Storage reads, and the desktop usage projection.

Solution scope and complexity

The solution is coherent for the stated requirements.

The added complexity supports:

  • Snapshot-revision fencing and retry behavior.
  • A bounded 32-attempt repair-settle wait.
  • Provenance for reported, derived, missing, and unpriced data.
  • Reconnect-safe IPC reads.
  • Regression fixtures for new data states.

Deletion and simplification opportunities

The removed legacy usage paths are appropriate because Host-backed reads replace them.

No further deletion is evident without weakening coverage for snapshot retries, repairs, provenance, pricing, token normalization, aborted tools, and UI states.

Risks and validation

User-visible Usage values, statuses, pricing, notices, and table columns change.

The Runtime Host compatibility epoch changes from 22 to 23. Public Usage and Host protocol contracts also change.

Reads can fail after repeated snapshot changes or incomplete repairs. Consumers of removed Storage APIs require migration.

The change summary reports linting, formatting, affected tests, Desktop build checks, and 129 Storybook scenarios. Direct check results are not available. The current shell context also reports no local diff, so the final required-check status remains unverified.

Review-relevant risks

The reported changes affect user-visible Usage behavior. Material changes in this area require independent human review under repository policy.

The reported changes affect public TypeScript contracts and Runtime Host protocol responses. Material changes in these areas require independent human review under repository policy.

The reported changes affect Runtime Host compatibility and release behavior. Material changes in these areas require independent human review under repository policy.

The person performing the merge reviews the final diff. A maintainer makes the final determination.

Walkthrough

The change moves Desktop usage retrieval to the canonical runtime-host authority. It adds revision-fenced snapshots, bounded repairs, provenance-aware values, aborted tool states, canonical token normalization, direct E2E fixtures, and coverage-aware usage UI.

Changes

Canonical usage flow

Layer / File(s) Summary
Usage contracts and canonical storage
packages/core/..., packages/runtime/..., packages/storage/...
Usage records preserve usage and cost provenance. Legacy token totals are normalized. Usage writers expose snapshot revisions.
Runtime-host protocol, repair, and snapshot queries
packages/runtime-host/src/protocol/..., packages/runtime-host/src/server/..., packages/runtime-host/src/__tests__/...
Runtime-host results require revisions. Queries perform bounded repairs, validate pagination metadata, and retry inconsistent snapshots.
Desktop IPC usage projection
apps/desktop/src/main/runtime-host-usage-ipc-main.ts, apps/desktop/src/main/runtime-host-boot.ts, apps/desktop/src/main/__tests__/runtime-host-usage-projection.test.ts
Desktop loads summaries, buckets, logs, and pricing from one validated snapshot and projects them into UsageStats.
Canonical usage fixture seeding
apps/desktop/src/main/e2e-fixture.ts, apps/desktop/src/main/e2e-fixture/scenarios-usage.ts
E2E setup seeds canonical model-call and telemetry stores with minimal transcripts and varied usage scenarios.
Coverage-aware usage presentation
apps/desktop/src/renderer/locales/settings-usage-copy.ts, apps/desktop/src/renderer/settings/usage-settings-page.tsx, apps/desktop/stories/settings/settings-pages.stories.tsx
The page displays coverage notices, basis-aware values, aborted statuses, missing session IDs, and aborted tool counts.

Estimated code review effort: 4 (Complex) | ~60 minutes

Merge Risk: 🔵 Low · up to 3776e

The Usage migration is otherwise mergeable, but a changed storage test can leave a background writer running when an assertion fails, potentially causing hung or misleading test runs; make the cleanup path unconditional before relying on that regression test.

Sequence Diagram(s)

sequenceDiagram
  participant UsagePage
  participant DesktopIPC
  participant RuntimeHost
  participant UsageStores
  UsagePage->>DesktopIPC: request usage range
  DesktopIPC->>RuntimeHost: query summaries, buckets, logs, and pricing
  RuntimeHost->>UsageStores: repair and read canonical snapshot
  UsageStores-->>RuntimeHost: usage data and revision
  RuntimeHost-->>DesktopIPC: validated pages
  DesktopIPC-->>UsagePage: projected UsageStats
Loading

Possibly related PRs

Suggested reviewers: astro-han

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Ai Use Disclosure ⚠️ Warning The PR description selects neither AI-use declaration, and none of the 13 introduced commit messages contains a valid Generated-by trailer. Select exactly one AI-use option and, if applicable, name the tool and scope; follow CONTRIBUTING.md’s “Human ownership and AI attribution” section.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue #2128 by preserving missing usage, retaining aborted tool status, using Host-backed consistent snapshots, and removing legacy fallback paths.
Out of Scope Changes check ✅ Passed The changes remain aligned with the linked issue and stated objectives, including pricing, repair, revision, protocol, UI, and legacy-model removal work.
Title check ✅ Passed The title clearly identifies the main change: Desktop Usage now reads from the Runtime Host.
Description check ✅ Passed The description includes the required summary, issue reference, verification results, AI-use declaration, checklist, behavior change, and supporting screenshots.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from 9fe90f2 to 39b071f Compare August 17, 2026 15:01

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/runtime-host/src/server/canonical-usage-reader.ts (1)

55-60: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider making repair required.

Both callers pass repair explicitly, and the coordinator already defines a NO_REPAIR sentinel for the no-reader case. The default silently reports pendingRepairs: 0, which is the one claim CanonicalUsageRepairStats exists to prevent a caller from making by accident. A required parameter forces each new caller to state its repair posture.

♻️ Require the repair posture
-  repair: CanonicalUsageRepairStats = { remaining: 0, unreadableEvents: 0 },
+  repair: CanonicalUsageRepairStats,

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dc7840c8-3d9d-4c30-bf0b-5851846306d0

📥 Commits

Reviewing files that changed from the base of the PR and between 5d9ce0d and 39b071f.

📒 Files selected for processing (30)
  • apps/desktop/src/main/__tests__/runtime-host-usage-projection.test.ts
  • apps/desktop/src/main/e2e-fixture.ts
  • apps/desktop/src/main/e2e-fixture/scenarios-usage.ts
  • apps/desktop/src/main/runtime-host-boot.ts
  • apps/desktop/src/main/runtime-host-usage-ipc-main.ts
  • apps/desktop/src/renderer/locales/settings-usage-copy.ts
  • apps/desktop/src/renderer/settings/usage-settings-page.tsx
  • apps/desktop/src/shared/desktop-session-projection.ts
  • apps/desktop/stories/settings/settings-pages.stories.tsx
  • packages/core/src/__tests__/model-call-usage-projection.test.ts
  • packages/core/src/model-call-usage-projection.ts
  • packages/core/src/settings.ts
  • packages/core/src/usage-stats/types.ts
  • packages/runtime-host/src/__tests__/hosted-execution-runner.test.ts
  • packages/runtime-host/src/__tests__/usage-pricing-client-correlation.test.ts
  • packages/runtime-host/src/__tests__/usage-pricing-protocol.test.ts
  • packages/runtime-host/src/protocol/index.ts
  • packages/runtime-host/src/protocol/usage-pricing.ts
  • packages/runtime-host/src/server/canonical-usage-reader.ts
  • packages/runtime-host/src/server/usage-pricing-coordinator.ts
  • packages/runtime/src/__tests__/cost.test.ts
  • packages/runtime/src/telemetry/record-llm-call.ts
  • packages/runtime/src/telemetry/types.ts
  • packages/storage/src/__tests__/settings-store-usage.test.ts
  • packages/storage/src/__tests__/usage-stores.test.ts
  • packages/storage/src/settings-store.ts
  • packages/storage/src/sqlite-usage-store.ts
  • packages/storage/src/telemetry-file-schema.ts
  • packages/storage/src/usage-stats-store.ts
  • packages/storage/src/usage-stores.ts
💤 Files with no reviewable changes (4)
  • packages/storage/src/tests/settings-store-usage.test.ts
  • apps/desktop/src/shared/desktop-session-projection.ts
  • packages/storage/src/usage-stats-store.ts
  • apps/desktop/src/main/runtime-host-boot.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 2 remain after this review.

Comment thread apps/desktop/src/main/runtime-host-usage-ipc-main.ts Outdated
Comment thread packages/runtime-host/src/server/usage-pricing-coordinator.ts
Comment thread packages/storage/src/usage-stores.ts
@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from 39b071f to b093aff Compare August 17, 2026 15:36
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Note

The previously reviewed commits are no longer reachable (likely due to a force-push or rebase), so CodeRabbit is performing a full review instead of an incremental one. This review may take a little longer.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cc8cf103-9b14-4652-a49b-ae57a41acfcd

📥 Commits

Reviewing files that changed from the base of the PR and between b093aff and 3776e16.

📒 Files selected for processing (4)
  • apps/desktop/src/main/__tests__/runtime-host-usage-projection.test.ts
  • apps/desktop/src/main/runtime-host-usage-ipc-main.ts
  • packages/storage/src/__tests__/usage-stores.test.ts
  • packages/storage/src/usage-stores.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/storage/src/usage-stores.ts
  • apps/desktop/src/main/runtime-host-usage-ipc-main.ts

Included review availability: Your plan includes up to 3 reviews per rolling hour; 0 remain after this review.

Comment thread packages/storage/src/__tests__/usage-stores.test.ts

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for consolidating Usage under the Runtime Host and removing the parallel desktop storage path. I reviewed this head with two independent @reviewer passes plus a read-only ollama-cloud/deepseek-v4-flash:high pass. The revision/provenance fence and explicit partial/unpriced coverage are good directions.

The integration currently breaks at the Desktop Host boundary in two places. First, settings:usageStats moved to scoped Runtime Host IPC, but the preload still invokes the old process-scoped signature. Second, the new projection returns Host-local session IDs to a renderer whose session identity is the composite desktop key. Together these make the Usage page fail to load, or make “open session” unroutable even after loading is fixed.

The first-principles design is one selected-Host request contract and one Host→Desktop identity projection. Pass the target scope before range, make the renderer choose the Host explicitly, and convert every optional Usage sessionId with that Host's desktop session-key function at the main-process projection boundary. Do not make the renderer infer Host identity from a local ID.

Current CI is green, but the added tests call the projection helper directly and do not cross the scoped IPC/preload/navigation seam. No local test suite was run during this review; conclusions are based on source, test, and current CI inspection. Codex coordinated the passes and performed the final adjudication; external-model output was treated as unverified until checked against the code.

中文摘要

感谢把 Usage 收敛到 Runtime Host 并删除 Desktop 的并行 storage 路径。revision/provenance fence,以及 partial/unpriced 的明确呈现,方向正确。

当前在 Desktop Host 边界有两个集成故障:settings:usageStats 已迁到 scoped Runtime Host IPC,但 preload 仍使用旧的 process-scoped 参数;新 projection 又把 Host-local session ID 直接交给需要 composite desktop key 的 renderer。结果是 Usage 页面无法加载,或修复加载后“打开会话”仍无法路由。

最小设计是一套 selected-Host request contract 和一处 Host→Desktop identity projection:scope 必须在 range 之前传入,renderer 明确选择 Host,并在 main projection 边界把每个 Usage sessionId 转成该 Host 的 desktop session key。不要让 renderer 从裸 local ID 推断 Host。

当前 CI 全绿,但新增测试绕过了 scoped IPC/preload/navigation seam。本次未运行本地测试套件;外部模型输出在核对代码前均视为未验证输入。

Comment thread apps/desktop/src/main/runtime-host-usage-ipc-main.ts
Comment thread apps/desktop/src/main/runtime-host-usage-ipc-main.ts Outdated
@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from ddc93c9 to 046520c Compare August 18, 2026 15:37

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The PR correctly keeps the AgentRun stream as the durable model-call authority and treats Usage as a projection. One compatibility defect remains: the strict wire shape changed without version isolation. The smallest fix is to make the compatibility epoch accurately describe the new response contract.

AI-assisted review disclosure: Codex verified the finding against the current head, strict protocol codecs, client collection, and live PR scope. Two independent reviewer-agent passes and an OpenCode Go DeepSeek V4 Flash (high) adversarial pass were used as inputs; findings outside this PR's effective diff were discarded. No local tests were run.

中文复核

让 AgentRun stream 作为模型调用的持久权威、Usage 只做投影,方向正确。当前保留一个兼容性问题:严格 wire shape 已变化,但 compatibility epoch 没有隔离新旧协议。最小修复是让 epoch 如实表达新的响应契约。

本次为 AI 辅助审查:Codex 在最新 head 上核验严格协议 codec、客户端收集逻辑与实时 PR 范围;另使用两次独立 reviewer 及一次 OpenCode Go DeepSeek V4 Flash(high)对抗审查,不属于本 PR 有效 diff 的 finding 已剔除。未运行本地测试。

Comment thread packages/runtime-host/src/protocol/usage-pricing.ts
@Astro-Han

Copy link
Copy Markdown
Contributor

/agentic_review

@qodo-code-review

qodo-code-review Bot commented Aug 18, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Fence revision wire change ✓ Resolved 🐞 Bug ≡ Correctness
Description
Fix-now: decodeUsageQueryResult now rejects every summary, bucket, and log response that lacks
revision, yet the Runtime Host protocol version and compatibility epoch remain unchanged. A newly
updated Desktop can therefore reconnect to an older Host that passes the unchanged handshake but
emits the previous response shape, causing Settings → Usage reads to fail during protocol decoding.
Code

packages/runtime-host/src/protocol/usage-pricing.ts[388]

+      'revision',
Relevance

●●● Strong

PR #3126 explicitly treated wire-shape changes as requiring a compatibility-epoch bump to prevent
decoder failures after reconnect.

PR-#3126

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The changed decoder makes revision mandatory, while the constants used by the connection handshake
still identify this build as compatible with Hosts implementing the previous, revision-less response
contract. The new Desktop Usage path issues several queryUsage calls, so decoding one such
response fails the entire request.

packages/runtime-host/src/protocol/usage-pricing.ts[383-437]
packages/runtime-host/src/protocol/index.ts[70-74]
apps/desktop/src/main/runtime-host-usage-ipc-main.ts[217-230]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`usage.query` now requires the new `revision` field, which changes the wire contract. The unchanged compatibility epoch permits a new Desktop to negotiate with an older Runtime Host that does not send that field, so all Usage reads fail decoding.

## Issue Context
The snapshot-consistency algorithm cannot safely be reused with a revision-less response, so accepting the prior wire shape is insufficient. Make the existing compatibility handshake reject this mixed-version pair before Usage IPC is used; this is the smallest correction and adds no new protocol surface or state.

## Fix Focus Areas
- packages/runtime-host/src/protocol/index.ts[70-74]
- packages/runtime-host/src/protocol/usage-pricing.ts[383-437]
- packages/runtime-host/src/__tests__/handshake-compatibility.test.ts[1-200]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Fixture owner lease leaks 🐞 Bug ☼ Reliability
Description
Fix-now: seedUsageStatsFixture() opens the Usage stores before entering its try/finally, so a
rejection from openInteractiveUsageStoresForWrite() skips owner.close() and leaves the
interactive-root ownership held. This can make subsequent fixture setup or retries fail to acquire
the root after an initialization error.
Code

apps/desktop/src/main/e2e-fixture/scenarios-usage.ts[211]

+  const stores = await openInteractiveUsageStoresForWrite(owner.lease);
Relevance

●●● Strong

PR #3111 accepted the identical owner-before-try leak pattern and moved acquisition-dependent
cleanup inside finally.

PR-#3111

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The fixture acquires owner at lines 207-210, but its only owner.close() is inside a finally
block that begins after the awaited open at line 211. The open function awaits repository
initialization and can reject, so that failure path bypasses the cleanup; past PR #3111 documents
and fixes the same owner-before-try leak pattern.

apps/desktop/src/main/e2e-fixture/scenarios-usage.ts[205-222]
packages/storage/src/usage-stores.ts[277-296]
PR-#3111

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`seedUsageStatsFixture()` does not release the acquired interactive-root owner if opening the Usage stores rejects.

## Issue Context
Declare the store conditionally, enter `try/finally` immediately after owner acquisition, open the store inside the `try`, and close the store only when it was created; always close the owner. This reuses the existing cleanup path and adds no new state or public surface beyond a local optional store reference.

## Fix Focus Areas
- apps/desktop/src/main/e2e-fixture/scenarios-usage.ts[205-222]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: 🧠 Deep: This is a high-density behavioral and architectural change spanning the Runtime Host protocol, IPC, storage removal, usage aggregation, pricing/cost semantics, and UI with 119 independent edit sites, making multiple subtle defects plausible across independent paths.

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread packages/runtime-host/src/protocol/usage-pricing.ts

@me2seeks me2seeks left a comment

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.

Publishing the verified inline fix responses.

Comment thread apps/desktop/src/main/runtime-host-usage-ipc-main.ts Outdated
Comment thread packages/storage/src/__tests__/usage-stores.test.ts
Comment thread apps/desktop/src/main/runtime-host-usage-ipc-main.ts
Comment thread apps/desktop/src/main/runtime-host-usage-ipc-main.ts Outdated

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The architectural direction is right: Runtime Host is now the Usage authority, the old Session scanner is gone, Desktop IDs are scoped correctly, and the protocol/revision boundaries are much clearer.

I’m leaving COMMENT because the canonical projection still has three related consistency gaps:

  • [P1] packages/runtime-host/src/server/execution-model-composition.ts:220-233 — unrecoverable authority/marker window. The AgentRun append commits first; the pending marker is written afterward and marker failure is swallowed. A crash between them—or marker failure followed by projection failure—leaves neither a Usage row nor a marker. Repair only scans markers even though the comment claims a lost marker is found by full reprojection, so spend can disappear permanently with pendingRepairs: 0. Recovery must be discoverable from a durable outbox/commit seam or a checkpointed authority sweep; add failure injection after the authority append.
  • [P2] packages/storage/src/model-call-ledger.ts:282-290 — empty authority reads erase the repair signal. Zero events decode as zero attempts/zero unreadable events, so the marker is cleared and the run is counted repaired. Treat an empty result as incomplete unless terminal absence is proven, retain the marker, and add an empty-authority regression.
  • [P2] Query-triggered repair is not shared across Desktop’s concurrent views. I left this one inline at the changed owner.

The first-principles end state is to keep Usage queries pure reads and give reconciliation one lifecycle owner that can discover authority records without relying on a best-effort marker. The existing historical-total provenance thread also remains valid but is not duplicated here.

AI-assisted review disclosure: two Codex reviewers independently reviewed exact head 1f3e1fb; Codex then traced the findings through the authority append, marker, repair, and Desktop multi-view read paths.

中文说明

整体方向正确,但当前仍有三个一致性缺口:authority append 与 marker 之间可能永久漏记;空 authority 读取会错误清除 marker;多个并发 Usage query 会各自触发 repair 并读取到不同的 pending 状态。最干净的终态是让 query 保持纯读,由单一 lifecycle/background owner 通过 durable outbox 或 authority checkpoint 完成 reconciliation。

Comment thread packages/runtime-host/src/server/usage-pricing-coordinator.ts Outdated
@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from 1f3e1fb to 8fe5c40 Compare August 19, 2026 10:57
@me2seeks

Copy link
Copy Markdown
Contributor Author

The lone red check is unrelated to this Usage delta: Desktop E2E passed 41 tests and failed only slash-command-menu.spec.ts › compacts the active session, timing out while waiting for Fake backend received: after compact. This PR does not change slash commands, compaction, composer submission, or the fake backend; the prior head passed the same E2E suite, while the new Usage-specific protocol 7/7 and Desktop projection 6/6 pass locally. I did not add an unrelated rewrite to this PR.

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for consolidating Desktop usage onto the Runtime Host authority. The current head preserves the intended authority and revision-fence boundaries, and the supplied Usage screenshots cover the visible change. I discarded two initially suspected repair-marker issues after confirming those paths are byte-identical to current main.

One new-call provenance P2 remains inline below. The earlier top-level finding about ambiguous legacy stored totals also remains valid and is not duplicated: a historical camelCase provider-reported total can lack raw snake-case evidence, so the new reader can silently rewrite it while the UI says legacy rows keep stored values. Please choose one explicit legacy invariant and add that regression.

Provenance is nearly complete, but substantive commit cba443c2 has no Generated-by trailer while the PR declares Maka assistance; please add the trailer or explicitly confirm that commit was human-only. The unrelated slash-command E2E timeout appears out of scope, but the required checks still need a green rerun before merge.

Reviewed with Codex as an AI-assisted code review. I verified the current diff against main, relevant telemetry paths, screenshots, CI, and commit provenance; no external model output was used.

中文说明

本 PR 的 Host authority、revision fence 和 Usage 截图方向正确;两个最初怀疑的 repair-marker 问题经核对与 main 完全一致,已排除。当前仍有一个新调用的来源错标 P2,以及此前已指出、这里不重复发 inline 的历史 total provenance 歧义。另请补 cba443c2 的 Generated-by trailer,或明确该 commit 为纯人工;合并前还需把 required checks 重跑至绿色。

Comment thread packages/runtime/src/telemetry/record-llm-call.ts Outdated
@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from 8fe5c40 to f202838 Compare August 19, 2026 12:16
@me2seeks

Copy link
Copy Markdown
Contributor Author

Review remediation is now on f20283805: provider-reported versus derived total-token provenance is preserved end to end, and ambiguous source-less legacy totals keep their stored value. Workspace typecheck, Storage full/focused, and focused Runtime usage tests pass. The PR body and rewritten history now distinguish Maka and Codex contributions, including the former cba443c2. Existing Usage screenshots remain present. Please re-review the current head.

Astro-Han
Astro-Han previously approved these changes Aug 19, 2026

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed exact head f20283805372930595a767e1d25822dca67d412a.

The usage provenance path now explicitly carries reported versus derived totals for new calls. For source-less legacy rows, the implementation adopts one clear invariant—preserve the stored total instead of guessing provenance and silently rewriting it—and the regression coverage follows that invariant through summaries and buckets. All review threads are resolved, all checks are green, the three requested Usage screenshots are available, and all 18 commits plus the PR body contain complete Maka/Codex provenance disclosure. I found no remaining P0–P3 issues in this PR.

AI-assisted review disclosure: Codex reviewed the exact-head diff, prior findings and remediation, focused tests, current CI and threads, screenshots, and provenance metadata. No external model was used. Astro-Han authorized this review campaign.

中文说明

新调用已完整区分 reported/derived total;无 provenance 的历史数据采用明确且一致的规则,保留已存储 total,不再猜测来源后静默改写。相关 summary/bucket 回归、线程、CI、三张 Usage 截图及 18 个提交的 AI trailer 均已核实,没有剩余 P0–P3。

@Astro-Han
Astro-Han dismissed their stale review August 19, 2026 12:59

Withdrawing this approval after the final current-main integration scan found a compatibility-epoch collision introduced by #3103. A replacement exact-head COMMENT with the concrete fix follows.

@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from 9cea014 to 808a588 Compare August 20, 2026 10:54
@me2seeks

Copy link
Copy Markdown
Contributor Author

Rebased onto latest main (e3177c22e) and resolved all conflicts. New head: 808a588a3.

Conflict resolutions:

  1. packages/storage/src/__tests__/settings-store-usage.test.ts (modify/delete): kept the deletion — this branch retires the storage-owned Usage store entirely, so main's edits to that test (8de1f29a9 fake-backend retirement) have nothing left to apply to.
  2. handshake-compatibility.test.ts: kept both main's new legacy-desktop-surface shim test and this branch's generalized previous-epoch rejection test.
  3. protocol/index.ts epoch: main advanced to epoch 27 for the Host-owned shell preference, so the Usage revision requirement now advances the epoch 27 → 28 (with the 27 comment preserved). The epoch assertion in protocol.test.ts was updated accordingly (> 27).

Re-ran affected suites on the rebased head — all pass, no failures attributable to main's changes:

  • @maka/storage: 838 pass / 0 fail
  • @maka/runtime-host: 1026/1026 pass
  • apps/desktop: 985/985 pass
  • @maka/core, @maka/runtime: build clean (0 type errors)

@me2seeks
me2seeks requested a review from Astro-Han August 20, 2026 11:26

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

COMMENT. The core architectural intent is right and I want to say that first: runtime-host-boot.ts drops the unscoped ipcMain.handle("settings:usageStats") and projectDesktopUsageStats, packages/storage/src/usage-stats-store.ts and SettingsStore.usageStats are gone, and the only surviving path is the scoped Host client. There is no second Usage source of truth left on the Desktop side. Everything below is about the machinery layered on top of that path, not about the direction.

The epoch is correct and I am closing my earlier thread on it: protocol/index.ts:75 is 28 against a base of 27, the comment states why (the required revision field and the new optional usageBasis on LlmUsageLogProjection change the response shape), and handshake-compatibility.test.ts now pins RUNTIME_HOST_COMPATIBILITY_EPOCH - 1 so it will not go stale on the next rebase. One thing to watch, not a finding: #3236, #3199 and #3133 are all also taking 28 from a base at 27, and #3282 shows 28 while stacked on #3236. The = 28 line itself merges cleanly because every branch writes identical text and each PR's own assert epoch > 27 still passes, so whichever lands second silently ends up sharing an epoch with the first. Whoever merges these needs to re-check the value at merge time; I filed #3313 about removing the class.

Two P1s, three P2s inline. The two P1s compound each other — the longer a full-window read takes, the likelier the fence under it moves — so I would treat them as one problem with two halves.

Three smaller things not worth their own threads:

  • Changing the range issues two complete snapshot loads. usage-settings-page.tsx:93-97 calls props.onReload(range) after updateUsage({ range }), and because usage is client-owned the same update also lands in setClientSettings, changing settings.usage.range, which the effect at settings-surface.tsx:583-589 lists as a dependency. The reload ticket makes that correct but not cheap, and it doubles the exposure to both P1s. Dropping either the explicit onReload or the dependency fixes it.
  • Duplication a smaller change would subsume. loadAllBuckets / loadAllLlmLogs / loadAllToolLogs are three near-identical paging loops differing only in their result guard; projectCustomPricingRows re-implements the source === "custom" filter already in the usage:pricing:list handler; and sameUsageSnapshot still cross-checks totalRequests against three independently summed row counts after already requiring revision equality across all five views.
  • Stale copy. settings-usage-copy.ts's toolEmptyBody still promises "calls, successes, errors, and average duration" in both locales now that an Aborted column exists.

One thing I looked at and am explicitly not filing: usageRevision is a per-facade in-memory counter starting at 0 (usage-stores.ts:335), so a facade re-created mid-read would let two queries report revision: 0 over different data and sameUsageSnapshot would accept a torn snapshot. I could not establish that a drain-and-reopen is reachable between two queries of one snapshot, so this is inference only. Seeding the counter from the Host epoch would remove the class cheaply if you happen to be in there.

Test gaps, none P-rated:

  • Pagination has no coverage at all — every fake response in runtime-host-usage-projection.test.ts:137-215 returns nextOffset: null, so roughly 150 lines of new paging, the mid-pagination revision guard, the nextOffset <= offset guard and the rows.length !== total guard are all unexercised.
  • SNAPSHOT_ATTEMPTS exhaustion is untested, and so is the Desktop's handling of a Host internal_failure — which is the first P1.
  • usage-stores.test.ts's "preserves ambiguous legacy totals and trusts explicit current provenance" passes identically with totalTokensSource deleted, because canonicalTotalTokens ignores it. It asserts pass-through, not provenance-sensitive behaviour, so the body's "normalized the legacy total-token contract" is not proven by any test.
  • The new coordinator test asserts an intermediate (pendingReprojections().length === 44) rather than a consistent Desktop-visible snapshot, which is why it does not reach the batch-boundary P2.

biome format is clean on all changed packages/** files. Note for anyone re-checking: apps/desktop/** and packages/ui/** are excluded from formatter.includes in biome.jsonc, and biome format --stdin-file-path does not apply that exclusion for a path that does not exist locally, so new desktop files produce false diffs that way.

AI disclosure: this review was assisted by Claude (Opus), which performed the initial code search and cross-checking. Every finding published here I re-derived myself against the source at 808a588a3, and I sharpened the batch-boundary mechanism and dropped the one finding I could not establish as reachable.

Comment thread apps/desktop/src/main/runtime-host-usage-ipc-main.ts
Comment thread packages/runtime-host/src/server/usage-pricing-coordinator.ts
Comment thread packages/runtime-host/src/server/usage-pricing-coordinator.ts Outdated
Comment thread apps/desktop/src/renderer/settings/usage-settings-page.tsx Outdated
Comment thread packages/storage/src/sqlite-usage-store.ts
The Host-backed Usage projection hardcoded an empty pricing list, so the
pricing tab showed no rows and a zero count even when the Host held custom
overrides. Fold the Host pricing snapshot into the same read and project
only custom entries, splitting each model key into provider and model.

Generated-by: Maka
usageSnapshotRevision looped until the write barrier stopped moving, so a
Usage query behind a continuous write stream (an active turn recording
model calls) could hang instead of failing fast. Bound the settle to 32
attempts and throw, letting the coordinator surface a read failure; a
regression test drives a continuous write stream and asserts the
rejection.

Generated-by: Maka
If assert.rejects fails, the setImmediate writer loop previously kept
running after withInteractiveRoot removed the temporary root, keeping
the process alive and masking the original assertion failure. Move the
stop, writer wait, and closes into a finally block.

Generated-by: Maka
Host:
- usage.query gains an optional snapshot ticket; one bounded repair pass is
  pinned to it, so a paginating Desktop snapshot can no longer trigger a
  fresh pass (and its revision bump) between pages.
- Fence exhaustion and unsettled revision reads now answer the typed,
  retryable usage_revision_changed outcome instead of internal_failure.
- groupBy:'tool' buckets carry the terminal-status breakdown
  (success/error/aborted), so clients do not page every tool row to build
  the tool table.

Storage:
- canonicalTotalTokens consumes totalTokensSource: explicit derived totals
  normalize to input + output at the frozen read boundary (reasoning is an
  output detail), while reported and source-less legacy totals pass through.
- usageSnapshotRevision's settle exhaustion is the typed
  UsageRevisionUnsettledError, classified as revision_unsettled.

Desktop:
- One paging loop for every Usage view; log views are capped at five pages
  per source, bounding snapshot round trips and payload. Aggregates still
  cover the full window, and the requests tab labels the capped view with
  the recorded total.
- The Host's fence-exhaustion outcome folds into the whole-snapshot retry;
  changing the range no longer issues two complete snapshot loads; the
  request rows display the stored canonical total so they reconcile with
  the summary and bucket totals.

Generated-by: Maka
@me2seeks
me2seeks force-pushed the fix/2128-host-backed-usage branch from e60fcf6 to edace14 Compare August 20, 2026 16:21
@me2seeks

Copy link
Copy Markdown
Contributor Author

All findings from the 2026-08-20 review are addressed at edace148a (rebased onto current main). Two P1s and three P2s are fixed with the regression tests each thread asked for; details inline.

Epoch note: main landed the relay Fast service-tier change as epoch 28 while this was in flight, so this branch now advances the usage wire contract from 28 to 29, keeping both comments. handshake-compatibility.test.ts still pins EPOCH - 1, so it cannot go stale on the next rebase. This is exactly the merge-time collision you warned about — worth re-checking #3236/#3199/#3133 against 29 now.

Minor items:

  • Double snapshot load on range change: setRange no longer calls props.onReload explicitly; the settings-surface effect on settings.usage.range is the single reload path.
  • Paging-loop duplication: loadAllBuckets/loadAllLlmLogs/loadAllToolLogs are now thin guards over one shared loadUsagePages; the custom-pricing filter is shared between usage:pricing:list and the snapshot projection; sameUsageSnapshot no longer cross-sums row counts after requiring revision equality (the cross-sums were also incompatible with capped log views).
  • Stale copy: toolEmptyBody mentions the Aborted column in both locales.
  • Not-filed revision seeding: left as-is — the per-facade counter lives in the storage layer, which has no Host-epoch reference to seed from, and I also could not establish a drain-and-reopen between two queries of one snapshot.

Test gaps, all closed:

  • Pagination: multi-page cap test (600 LLM + 200 tool rows → exactly 5 + 2 page requests, bounded payload, logsTotal/logsTruncated correct), mid-pagination revision change, non-advancing nextOffset, and a short final page.
  • SNAPSHOT_ATTEMPTS exhaustion and the Host busy-authority outcome: Desktop folds usage_revision_changed into the retry and surfaces failure only after three attempts; Host answers the typed outcome under a continuous admitted-write stream.
  • Provenance: the storage test now fails if totalTokensSource is deleted (a derived row stored as 37 reads back as input + output = 30 everywhere).
  • Repair batch boundary: a paginating snapshot over a 60-run backlog asserts one repair pass spans all pages, with equal revisions and uniform pendingRepairs.

CI: the test lane is green. The package (Release Windows check) failure is repo-wide harness flakiness, not this diff: the docs-only branch docs/3270-third-party-attribution fails the identical Packaged Maka renderer did not expose CDP within 30 seconds: fetch failed error (job 96471003805), feat/windows-installer-rollback fails and passes on the same branch within the same window, and fix/windows-verify-harness exists to repair the harness. Local suites: runtime-host 1034/1034, storage 839/839, core 558/558, desktop main 992/992; biome format/lint clean on all changed packages/** files.

中文说明

本轮 review 的 2 个 P1、3 个 P2 均已修复并附各自要求的回归测试(见各 thread 回复);三个小项(范围切换双次加载、分页循环重复、pricing 过滤重复、过时文案)也已落地。由于 main 在此期间把 relay Fast service-tier 占为 epoch 28,本分支 rebase 后把 usage 协议推进到 29 并保留两边注释。package 通道的 CDP 30s 失败是全仓 harness flake(纯文档分支同错),与本 diff 无关;本地四个套件全绿。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Usage hides model attempts without token data and marks interrupted tools as successful

2 participants