Skip to content

feat(inspector): say what the context is full of, not just how full - #2346

Merged
Astro-Han merged 4 commits into
apache:mainfrom
ARE404:are404/feat-prompt-composition
Aug 13, 2026
Merged

feat(inspector): say what the context is full of, not just how full#2346
Astro-Han merged 4 commits into
apache:mainfrom
ARE404:are404/feat-prompt-composition

Conversation

@ARE404

@ARE404ARE404 commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

What

The Inspector's context bar says how full the context is. It could not say what filled it, so a reader looking at 84% had nothing to act on. This adds that answer — per kind, and per tool, because "tool definitions are 40%" names nothing to remove while "Bash ≈2,350" does.

It lands on context.diagnostics.query, the Host operation that already owns "what is the current context made of" and that /context already prints (#1580). Not on SessionTrace: that ledger answers what happened and what it cost, and widening it would have put a per-request fact on every attempt of every turn.

The request is the only authority. A completed main call's canonical ModelCallAttempt is the durable commit; the latest_context projection is written by that same storage transaction (AgentRunAppendOptions.latestContext) and is a product of it, never a second record racing it. latest_context is a projection key nothing can append under. Failed, aborted and compaction calls commit their metering alone and leave the last good answer standing.

One commit object crosses every layer.ModelCallCommit { attempt, latestContext } travels Tracker → AiSdkBackend → Runtime Host adapter → RuntimeKernel → AgentRun as one value, so a layer that forwards only the attempt is a type error rather than a silently missing feature.

Facts are bound where they are true. The compaction boundary is captured beside each physical provider dispatch — one tracker spans every request of a send, and mid-turn compaction or overflow recovery prepares later requests against different boundaries. The boundary reported is the checkpoint the projection was actually replayed through, not one the policy merely carries: a loaded checkpoint can be refused and still sit in the policy.

Bytes cross the wire; estimates do not. The Host returns measured bytes. ≈ bytes / 4 is a display rule made where it is shown — by /context and by the panel — and every figure carries . A figure rounded into the contract could no longer be labelled as an estimate. The tool list is bounded at the fold (top 64 plus a remainder carrying count and bytes), not at the wire decoder: a single MCP server may advertise 1000 tools, so a decoder cap only moves the cliff.

Absence stays visible. Metering is durable; the capture carrying the segments is best-effort. "Metered, with no composition on record" is reachable, and it renders as a stated gap rather than a prompt made of nothing. A request either explains itself or says nothing — it never wears an older request's breakdown.

Warm reads are O(1). Both the projection write and the cold rebuild use one shared (completedAt, attemptId) rule, so a request that finished earlier cannot move the answer backwards when overlapping turns append out of order. A cold rebuild repairs the projection on its way out — including a damaged row, which the generic repair policy used to preserve forever, leaving long sessions rescanning their whole ledger on every refresh.

Ledgers written before canonical metering fall back to their completed provider attempt, and only when the scan found no canonical record at all.

Not in scope, as agreed on the issue: splitting message by what produced it. At the provider seam messages arrive already serialized, so that attribution has to come from upstream. The system-prompt/ split stays available as its own question.

Surfaces

  • Inspector → 追踪 (Trace) gains an estimated-composition block under the context bar: per-kind rows, per-tool rows, a folded remainder, an unnamed-tools row for ledgers written before tool labels, and an unrecorded state.
  • CLI /context prints the same snapshot, now including the per-tool rows and the remainder.

Validation

  • Repo gates: npm run build:test, npm run format:check, npm run lint, all four desktop typecheck projects.
  • Focused suites: composition fold, context diagnostics (warm read, monotonicity under inverse completion order, subagent isolation, legacy provider-only ledgers, damaged-projection repair, tool bound), the production-chain commit test, provider-request telemetry.
  • Every new test was shown to fail without its own fix by patching the compiled output and re-running — including the production-chain test, which fails if any layer drops latestContext.
  • Two failures in the author's worktree are environmental and pass in CI: five file-tool tests need rg, and one storage test needs node_modules/dugite/git/bin/git.

Evidence

The previous build had no corresponding section at this location, so the comparison is "previously absent" against the new populated state. Screenshots follow in a comment.

@ARE404

Copy link
Copy Markdown
ContributorAuthor

Ran an adversarial pass over my own diff before asking for review time. One real defect, three cleanups, and one measured number I'd rather you decide on than settle myself. All in 378dcd6.

The defect. The tool block was gated on tools.length > 0, so a composition whose segments carry no label rendered no tool rows at all — including the unnamed row that exists precisely to keep those bytes visible. That is every session recorded before this branch: the label is new, so old ledgers have tool bytes and no names, and the one row that would have said so was the row being hidden. Fixed, with a TraceUnnamedTools story and a view-model test on it. Storybook now renders 按工具 → 未命名的工具 ≈10,500 against a 工具定义 ≈10,500 total, so the bytes reconcile instead of vanishing.

The cleanups.

  • estimatedTotalTokens was derived in the view model and rendered nowhere — the same "computed but never rendered" you caught on feat(inspector): surface the session trace as a workbar tab #2018. The total stays on the contract as the measured fact; the derivation is gone.
  • A zero-byte part rendered as ≈0. /context drops those (value > 0), and a part nothing contributed to is not a part.
  • The ledger decoder fabricated index: 0 and hash: '' to satisfy PreparedRequestSegment, neither of which the fold reads. The fold now takes SizedRequestSegment — the three fields it uses — which a live capture satisfies structurally and a decoder can produce without inventing anything.

Also added the one test that runs a real capturePreparedProviderRequest into foldPromptComposition. Every other test in that file writes its own segments, so a field renamed on one side would have passed all of them. It passes, so the names do line up — but it was the test that could have failed.

The number I want your call on. Composition on every attempt is not free, and I measured rather than guessed (40 tools, 60 attempts):

bytes
composition per attempt1,677 B
the attempt record it hangs off324 B
added over a long session~98 KiB
composition without the tool list180 B

So the per-tool list is ~90% of it, and the panel reads it for exactly one attempt — the latest metered main call, the one the bar measures. The other 59 copies are correct, nearly identical to each other, and unread.

I left it whole because trimming it means putting "which attempt is worth explaining" into the projection, and that is a view judgement the contract shouldn't be making — the same reason contextBudget picks the latest attempt in the panel rather than in the trace. But the waste is real and measured. If you'd rather the read attached the tool list only where it will be rendered, that is a filter in readSessionTrace and I'll take it in this PR.

@ARE404
ARE404force-pushed the are404/feat-prompt-composition branch from 378dcd6 to 8c6d700CompareAugust 7, 2026 08:39

@Astro-HanAstro-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 — the self-review round was genuinely good: the unnamed-tools fix closes the old-ledger hole for real (the gate now covers unlabelled bytes, verified against every reachable legacy shape), the three cleanups hold up, and the live capture→fold test pins the write side.

On the number you left for us: keep the full composition. The projection is already complete for every attempt, /context reads the same events and can reuse foldPromptComposition for per-tool rows, and picking "the latest attempt" inside the trace would duplicate a view rule into the read path. If you ever want to trim, the right lever is a cap on the tool list in the fold (top-N + a remainder row) — not a filter in the read.

Three P2s to consider:

  1. Runtime-host mode never reads composition events — execution-inspect-coordinator.ts:276 only walks MODEL_CALL_ATTEMPT_EVENT_TYPE and never passes promptCompositions, so with MAKA_DESKTOP_RUNTIME_OWNER=runtime-host the panel claims "no composition on record" for every session, even when the ledger has the events. The embedded path (inspector-ipc-main.ts:76-101) does the full walk; the fix there is the same walk plus one branch — cheap.
  2. A completed call without contextWindow metadata makes the whole section silently vanish — not even the unrecorded state shows, which contradicts the doc's "absent only when there is no call to ask about". Either decouple the latest-attempt selection from the window filter or correct the doc.
  3. No round-trip test through the decode seam — readSegment reads value.label without a type boundary, and every fixture on the decode side is hand-written, so a rename on one side still passes everything. A capture → JSON → readPromptCompositionEvent round-trip would close it.

Nits: InspectorComposition.totalBytes is a pass-through nothing renders (same "derived but unread" class you just cleaned up), zero-byte drop has no test, unrecorded has no story, and the "total still adds up" comment overclaims for the token column (ceil per row vs per sum).

@ARE404

Copy link
Copy Markdown
ContributorAuthor

Thanks — taking all three P2s and the nits. Rebased onto 76ff58803 first, and that rebase turned P2-1 into something bigger than a P2, so I want your call before I build it.

P2-1 is now load-bearing, not optional.#2420 (M5 production cutover) deleted inspector-ipc-main.ts and its tests outright — the embedded path I wired composition into no longer exists. ExecutionInspectCoordinator is the only path left, so without the Host-side read the feature is not degraded, it is simply absent everywhere. Rebase resolved by accepting those deletions; the two tests they carried need re-homing in runtime-host regardless.

But "the same walk plus one branch" doesn't hold on the surviving seam, and I'd rather say so than quietly ship a regression. The embedded read walked every event of every run with no ceiling. #readSessionTrace (execution-inspect-coordinator.ts:251-304) does not: every read goes through InspectEvidenceBudget, capped at EXECUTION_INSPECT_EVIDENCE_MAX_RECORDS = 4096 / EXECUTION_INSPECT_EVIDENCE_MAX_BYTES = 512 KiB for the whole session trace (protocol/execution-inspect.ts:28-29), and exceeding it throwsInspectQueryTooLargeError rather than truncating — deliberately, since a silently short evidence record is the thing this track refuses.

Adding a third bounded read per run would spend that budget on the fattest events in the ledger. Measured with a realistic request (40 tools, 60 messages) through the real capturePreparedProviderRequest:

segments in one request102 (≈158 B each)
stored segments per attempt15.7 KiB
10 attempts157 KiB — 31% of the session budget
30 attempts472 KiB — 92%
60 attempts943 KiB — 184%, the trace fails

So the naive port doesn't add a breakdown to the Inspector; it turns a working Inspector into stop the Host to inspect it offline for ordinary working sessions. The capture event is expensive precisely because it enumerates every tool and every message with a hash — which is also why folding it is worth doing, but the fold happens after the read that blows the budget.

Three ways I can see, none free:

  1. Leftover budget, degrade instead of failing. Read compositions last, from whatever budget remains, and treat exhaustion as "not read" rather than an error, so composition can never fail the trace. Smallest change, no storage work. Cost: the panel needs a state distinct from unrecorded — "we didn't read it" and "the ledger has none" are different facts, and reusing the existing copy would make the panel say something false.
  2. Store the fold, not the segments. Have the runtime persist the folded composition (~1.7 KiB) alongside or instead of the raw segments, so the read is cheap by construction. Cost: changes what is persisted — the retention question your issue opened with — and old ledgers still carry only the raw shape, so both have to be readable.
  3. Read newest-first. Cap by construction by reading only the most recent capture events. Cost: readEventsByTypeBounded has no direction parameter today, so this widens the PR into @maka/storage's interface and every implementation of it.

I lean 1 — it is the only one that cannot make an existing trace worse, and it keeps the budget's single ceiling intact. It does mean a fourth composition state on the panel; I'd rather add that than have the UI claim a call left no record when the truth is we stopped reading.

Happy to be overruled toward 2 if you'd rather pay it in storage once than in state forever — you own the retention call. Meanwhile P2-2 (the section vanishing when no attempt carries contextWindow), P2-3 (the capture → JSON → decode round-trip), and the nits (totalBytes pass-through, zero-byte drop test, unrecorded story, the "adds up" comment overclaiming for ceil-per-row) are queued and independent of this.

@Astro-Han

Copy link
Copy Markdown
Contributor

Thanks for stopping at the boundary question rather than quietly porting the old read. After checking current main, I think the rebase exposed a simpler owner than any of the three SessionTrace-local options.

I don’t think prompt composition should be widened into execution.inspect at all. The panel happens to live in Inspector, but the fact it needs is “what is the current context made of,” and we already have a typed owner for exactly that: context.diagnostics.query. Desktop already exposes it, /context already consumes it, and its result already carries the latest request’s input/window and four-part breakdown.

The existing implementation under that operation does need the same repair this feature has now made measurable: readLatestContextDiagnostics() is still an O(session ledger) scan. It also reads only the best-effort provider-attempt records, so if the latest capture append is missing, it can select an older request and present it as current. That was tolerable as an occasional command-time path, but it is not a good basis for the Inspector’s event-driven refresh.

My preferred end state would be:

  • keep the tool label at the provider seam, as this PR already does;
  • keep SessionTrace focused on causal history and canonical metering—no composition on every attempt and no third evidence read;
  • maintain a bounded, rebuildable latest-context projection, aligned to the latest durable main attempt by attemptId;
  • extend context.diagnostics.query with the measured per-part/per-tool bytes;
  • have both /context and the Inspector overview consume that same snapshot;
  • when durable metering exists but the best-effort composition does not match, report unrecorded rather than borrowing an older capture.

That also means the estimate can remain a renderer concern (≈ bytes / 4), while the Host returns measured bytes. Old ledgers could use the existing projection/repair seam once, rather than being rescanned on every visible-panel refresh.

Option 1 is still the smallest safe patch inside the current design, but I would avoid making “not read because the Inspector budget was exhausted” a permanent product state. It hides the feature precisely on the long sessions that need it most and leaves us reading many historical compositions to display one.

I think reshaping around the existing Context Diagnostics owner should remove more machinery than it adds: no PromptComposition expansion across the core trace contract, no per-attempt map/join in projectSessionTrace, and no composition-specific evidence-budget state. Would you be open to rebasing and taking the PR in that direction? I’d be happy to review against that shape.

@ARE404
ARE404force-pushed the are404/feat-prompt-composition branch from 8c6d700 to 59ed247CompareAugust 10, 2026 03:35
@ARE404

Copy link
Copy Markdown
ContributorAuthor

Reshaped as you described, rebased onto 76ff58803. You were right that this removes more than it adds — and the rebase itself turned out to be the argument for it.

Composition is gone from the trace.PromptComposition and its validators are out of the core contract, the promptCompositions input/map/join is out of projectSessionTrace, and there is no composition-specific evidence read anywhere. The tool label at the provider seam is all that stayed.

It now lives on context.diagnostics.query. Both the Inspector overview and /context read that one snapshot — the CLI gained the per-tool rows for free, since it was already printing the four-part breakdown from the same source.

Two repairs in the path it moved to, both pre-existing:

  1. readLatestContextDiagnostics was an O(session ledger) scan per call. Both ledger types are now latest-of-type projections, so a warm read is O(1) and the scan is a cold path that repairs the projection on its way out — old ledgers pay it once. That meant generalising the append-time projection maintenance in agent-run-store.ts, which was hardcoded to the checkpoint type; it is now a three-entry set, skipped for subagent runs. That last part is not incidental: the old reader filtered with isSessionInlineRun for a reason, and a session-level projection that ignored it would let a subagent's request be reported as the session's context. The run header is already loaded in that transaction, so the check is free.
  2. The reader took its identity from the best-effort capture, so a missing latest capture promoted an older request to "current" — the bug you named. The durable ModelCallAttempt is the anchor now, and the capture only describes the request it matches by attemptId. A request either explains itself or reports nothing. A compaction call is also excluded from the anchor: the summariser's prompt is a real prompt, but it is not the conversation's context.

Bytes cross the wire, bytes / 4 does not.estimatedTokens is off the protocol frame; each surface makes its own -labelled estimate. One rule, at the layer that shows it.

Why I did not port the old read to the Host path, in case it is useful for the next feature that wants to:ExecutionInspectCoordinator's reads go through InspectEvidenceBudget (4096 records / 512 KiB per session trace, and exceeding it throws rather than truncating). Measured through the real capturePreparedProviderRequest — 40 tools, 60 messages — the capture events are 15.7 KiB per attempt: 30 attempts is 92% of that budget, 60 is 184%. A third bounded read there would have converted a working Inspector into stop the Host to inspect it offline for ordinary sessions. Your reshape sidesteps it entirely, since the projection read is O(1) and touches one row.

Review follow-ups, all closed:

  • P2-2 is structural now rather than patched: composition never depended on the bar's attempt, so a call that reports no contextWindow no longer takes the section down with it. The doc comment that claimed otherwise is corrected rather than left to outrun the code.
  • P2-3: a capture -> JSON -> readPromptCompositionEvent round-trip, the seam where every hand-written fixture used to agree with itself.
  • Nits: the unrendered totalBytes pass-through is gone from the view model, a zero-byte part is dropped rather than shown as ≈0 (matching /context), unrecorded has its own story, and the "adds up" comment now says which column adds up — the bytes do, the ceil-per-row tokens cannot.

Validation: runtime 62/62 across the composition, diagnostics, projection and request-shape suites; CLI 141/141; typecheck (all four projects), format:check, lint, check-dead-css, check-console, check-a11y, check-copy, check-story-annotations all pass. Storybook verified for all three composition states — populated, unnamed-tools, and unrecorded.

One thing I did not do: apps/desktop has no inspector tests to update, since #2404/#2406 removed them all. I added the new coverage in @maka/runtime where the logic now lives rather than reinstating a desktop suite you had just trimmed — say the word if you would rather have the view-model judgements pinned too.

@Astro-HanAstro-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 reshaping this around context.diagnostics.query — moving composition out of SessionTrace, keeping measured bytes on the wire, and sharing one owner between Inspector and /context are all the right direction.

I think the remaining issues come from one abstraction mismatch rather than several isolated bugs:

P2 — The three latest-of-type rows do not form one latest-context snapshot. Failed, aborted, or compaction attempts can replace the completed-main row and force every later query back to a full ledger scan. A newer unmatched capture can hide the matching composition that still exists for the durable anchor. The checkpoint row also belongs to recovery, with different scope and ordering rules; reusing and repairing it here can attribute a child checkpoint to its parent or alter resume-derived state.

Rather than patching each sequence, I suggest one bounded, rebuildable LatestContextDiagnosticsProjection, replaced only when a durable main attempt completes. It should freeze the attempt identity, metering/window/cache facts, matching folded composition, and applicable compaction boundary. Failed, aborted, and compaction calls should not replace it, and the recovery checkpoint projection should remain private to its current owner. For older ledgers, a provider-attempt fallback can be used only when no durable model-call records exist.

P2 — The bar and composition can describe different attempts. The bar selects the latest trace attempt with a context window, while composition comes from the latest diagnostics attempt. If the newest call lacks a window, the bar shows A and the breakdown shows B. Please derive both from the same diagnostics snapshot, or join them explicitly by attemptId. Diagnostics should also remain visible when the trace read is empty or fails.

P2 — 257 tools make the whole query fail. The producer emits an unbounded tool list, while the Host decoder rejects more than 256; MCP already permits up to 1,000 tools per server. Please bound this at the fold/contract owner and carry top-N plus remainder count/bytes, rather than only raising the decoder limit.

P3 — Refresh temporarily clears a valid composition.useSessionTrace.load() preserves the previous trace but drops same-session context until the second request returns. Preserving both removes the visible flicker and makes the independent reads settle deterministically.

A few useful simplifications fall out of this shape: composition.totalBytes, the view-model’s unrendered byte/key pass-throughs, and one of the overlapping live-capture tests can go. I would keep the remaining fold tests, but replace the happy-path projection mocks with real warm-path tests for failed attempts, mismatched capture, child checkpoints, repeated reads, and the tool bound.

Finally, the current head conflicts with main. During rebase, please put both Inspector reads on main’s reconnectable IPC seam (handleReconnectableRead / tryReconnectableReadResult) and rerun CI against the new head.

The feature direction looks good; I’d be happy to re-review once the projection is made atomic at the question’s actual owner.

中文对照

感谢你把实现调整到 context.diagnostics.query。composition 不再进入 SessionTrace、Host 只传递实测字节、Inspector 和 /context 共用同一个 owner,这些方向都是对的。

目前剩余的问题,本质上不是几个孤立边界,而是 projection 还没有真正表达“同一次请求的完整上下文快照”。

P2 — 三条 latest-of-type 记录无法组成一个可靠快照。 失败、中止或 compaction attempt 会覆盖最近一次成功的 main attempt,使后续查询反复扫描整个 ledger;较新的孤立 capture 也会遮住仍然存在的正确 composition。checkpoint projection 原本服务于恢复流程,作用域和排序规则都不同,在 diagnostics 中复用和修复它,可能把子代理的 checkpoint 归给父会话,甚至改变 resume 依赖的派生状态。

与其逐个修补这些时序,建议由 Context Diagnostics 维护一个有界、可重建的 LatestContextDiagnosticsProjection,只在 durable main attempt 成功完成时替换,并一次性冻结 attempt 身份、metering/window/cache、匹配的 folded composition 以及当时适用的 compaction boundary。失败、中止和 compaction call 不应覆盖它,恢复流程现有的 checkpoint projection 也应继续由原 owner 独占。对于旧 ledger,只在确认完全没有 durable model-call record 时,才使用旧 provider attempt 做兼容回退。

P2 — 上方 bar 和下方 composition 可能来自两个请求。 bar 会跳过没有 contextWindow 的最新请求,composition 却仍描述最新请求,于是页面可能同时展示 A 的使用率和 B 的构成。建议两者都从同一 diagnostics snapshot 推导,或者至少通过 attemptId 明确关联。即使 trace 为空或读取失败,成功返回的 diagnostics 也应该能独立展示。

P2 — 第 257 个工具会使整次查询失败。 runtime 生成的工具列表没有上限,而 Host 协议拒绝超过 256 项;MCP 单个 server 又允许最多 1000 个工具。建议在 fold/contract owner 处统一做 top-N 加 remainder count/bytes,而不是简单调高 decoder 限制。

P3 — 刷新时会短暂清空已有 composition。useSessionTrace.load() 保留了旧 trace,却会先删掉同 session 的 context,直到第二个异步请求返回。刷新时同时保留两者即可消除闪烁。

这个形态也能顺便删掉一些没有实际消费的内容:composition.totalBytes、view-model 中未渲染的 byte/key 透传,以及一个重复的 live-capture 测试。其余 fold 测试建议保留,但 projection 测试应换成真实 warm-path 场景,覆盖失败 attempt、capture 失配、child checkpoint、连续读取和工具数量边界。

当前 head 还与 main 冲突。rebase 时,请让 trace 和 context 两个 Inspector 查询都使用 main 最新的 reconnectable IPC seam,并基于新 head 重新跑 CI。

整体功能方向很好;projection 收敛到真正的问题 owner 后,我们很愿意继续复审。

@ARE404
ARE404force-pushed the are404/feat-prompt-composition branch 3 times, most recently from 0acb031 to d422ab2CompareAugust 11, 2026 15:09
@ARE404

Copy link
Copy Markdown
ContributorAuthor

Done, rebased onto 109a931f4. Your diagnosis was the right one — three latest-of-type rows never were a snapshot, and each sequence I would have patched was a symptom of that.

One sealed record.latest_context_snapshot_recorded is written when a completed MAIN call settles, freezing that request's identity, its provider-reported numbers, the folded composition of its own capture, and the compaction boundary that applied — at one moment, so no two fields in it can describe different requests. Failed, aborted and compaction calls never reach the write, so the last good answer stands instead of being replaced by one that answers a different question. A read is one projection row; a ledger written before this record assembles one from the records it was sealed from, once, and only when no durable model-call record era exists.

Recovery's projection is untouched. The compaction boundary comes from asking historyCompactCoordinator, its owner — never from reading or repairing the row that owner maintains. That also removes the child-checkpoint hazard at its source rather than by ordering rules.

The bar and the composition now come from that same snapshot. They were selected separately, which is exactly how a newest call without a contextWindow put one request's fullness above another request's contents. latestMeteredMainAttempt is gone from the view model; diagnostics also render when the trace is empty or fails.

The tool list is bounded at the fold — top 64 plus a remainder carrying count and bytes, so the rows still account for every tool byte. Not at the decoder: with 1000 tools per MCP server permitted, a decoder cap only moves the cliff.

Refresh preserves the composition it already has. It preserved the trace and dropped the context, so a valid breakdown blanked on every ledger event until the second request returned. A failed context read now leaves the previous snapshot standing rather than reporting "no composition" for a read that merely failed.

Both Inspector reads are on main's reconnectable seam. composition.totalBytes, the view model's unrendered byte/key pass-throughs and the overlapping live-capture assertions are gone.

Tests are warm-path against the real store, as you asked: a failed attempt not replacing the snapshot (and the read staying warm afterwards), a subagent's run never becoming the session's context, a mismatched capture reporting nothing rather than an older request, a legacy ledger rebuilding once, and the tool bound with its bytes reconciling. The fold tests stayed.

Two things I got wrong in this round, both mine and both fixed:

  1. The totalBytes removal missed packages/cli. That broke the build, and with it typecheck, every test lane and e2e — all inside a minute. I had been verifying package by package; a contract change crossing a package boundary is invisible that way, so this round was checked with the repo's own npm run build:test.
  2. I ran biome format --write . over the whole repo, which swept 58 unrelated files into the diff. Two of them changed upstream afterwards, the PR stopped being mergeable, and GitHub creates no run for a head it cannot merge — so the "no checks reported" I saw was not a missed trigger. Reverted; the diff is 31 files.

On the current red:typecheck fails at npm run format:check and e2e times out in Desktop e2e. main fails the same two jobs at the same steps with the same errors right now (run 31504693950), and fix/biome-format-main appears to be the fix in flight. I have not touched those files — they are not mine to fix inside this PR. I will rebase once that lands.

Everything else is green: test_workspaces, test_runtime_host, test, storybook, windows_recovery, windows_baseline. Locally: full build:test, runtime-host 846/846, CLI 320/320, @maka/core 745/745, the composition and diagnostics suites 48/48, the desktop hook 5/5, and the Storybook states (populated, unnamed tools, unrecorded, near-limit) checked by hand — the near-limit story needed moving to a near-limit snapshot, since the bar reads that now.

@ARE404
ARE404force-pushed the are404/feat-prompt-composition branch from d422ab2 to 228f0d4CompareAugust 12, 2026 03:03

@Astro-HanAstro-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 rebasing and working through the previous round. I reviewed the current head (f9747b7) with several independent adversarial passes. CI is green, and the overall product direction is sound: context.diagnostics.query is the right shared Host operation, measured bytes stay on the wire while token estimates remain a presentation concern, and the CLI and Desktop are now intended to describe the same completed request.

My remaining concern is the authority model.

The sealed snapshot was introduced to avoid joining unrelated “latest” records at read time, but it is currently written as a second append-only ledger record after the canonical model-call attempt. That makes a derived duplicate into a parallel authority. The two writes can fail independently, arrive out of order across overlapping runs, and capture session-global state that did not belong to the request.

I think the clean end state is:

  1. The canonical completed-main attempt remains the only durable commit for the request.
  2. The same storage transaction monotonically updates a rebuildable latest_context projection using that attempt and the request-bound composition/compaction facts.
  3. Cold legacy reconstruction repairs that projection once.
  4. The standalone latest_context_snapshot_recorded event, its best-effort queue/callback, and its separate event-decoding path are removed.

This is a fairly large diff—30 files and about 2.2k changed lines—but I would not split it by Runtime, protocol, CLI, and Desktop. Those layers form one end-to-end behavior, and merging them separately would create temporary incomplete contracts. I would instead reduce this PR structurally by deleting the parallel snapshot path. A small storage primitive could be a preparatory PR only if it is independently useful and independently testable; otherwise keeping the root correction in this PR is simpler.

A few tests can also be simplified while doing that:

  • Replace the test named “rebuilds once”; it currently performs only one read and therefore cannot establish that claim.
  • Merge the overlapping prompt-composition capture and JSON-roundtrip fixtures into one end-to-end roundtrip test with explicit names, kinds, and byte totals.
  • Remove the unused checkpointEvent helper.
  • Prefer focused invariant tests for atomic commit failure, inverse completion order, provider-only legacy data, request/checkpoint interleaving, more than 64 tools, and diagnostics succeeding while trace loading is empty or failed.

All findings below are P2 and non-blocking. I’m approving this revision while leaving the inline comments as concrete recommendations toward a smaller, single-authority implementation. Please feel free to push back where an existing guarantee or constraint changes the analysis.

AI-assisted review disclosure

Codex performed the code inspection and independent adversarial review passes. I, Astro-Han, reviewed the resulting evidence, selected and framed the findings, and made the final review decision. I did not independently rerun every failure sequence described below.

中文对照

感谢作者完成 rebase 并处理上一轮反馈。我针对当前 head(f9747b7)进行了多路独立的对抗性复审。CI 当前全部通过,整体产品方向是正确的:context.diagnostics.query 是合适的 Runtime Host 公共入口;实测字节数保留在协议中,而 token 估算留在展示层;CLI 和 Desktop 也开始描述同一次已完成请求。

我目前主要关注的是 authority 模型。

sealed snapshot 的目的是避免在读取时拼接彼此无关的“最新记录”,但目前它是在 canonical model-call attempt 之后另外追加的一条 ledger record。这使得一份派生副本成为了平行权威。两个写入可以独立失败,也可能在重叠 run 之间乱序到达,还可能捕获并不属于该请求的 session-global 状态。

我认为最干净的最终状态是:

  1. canonical completed-main attempt 仍然是该请求唯一的 durable commit。
  2. 在同一个 storage transaction 中,根据该 attempt 以及与请求绑定的 composition/compaction 信息,单调更新一个可重建的 latest_context projection。
  3. legacy cold rebuild 完成后修复该 projection,使后续读取保持 O(1)。
  4. 删除独立的 latest_context_snapshot_recorded event、best-effort queue/callback,以及单独的 event decoding 路径。

当前 diff 涉及 30 个文件,约 2.2k 行变更,确实偏大,但我不建议按 Runtime、protocol、CLI 和 Desktop 拆分。这些层共同组成一个端到端行为,分别合并会产生暂时不完整的合同。更好的缩减方式是在本 PR 内删除平行 snapshot 路径。如果某个 storage primitive 本身具有独立用途并能独立验证,也可以作为准备性 PR;否则把根因修正在本 PR 内会更简单。

测试方面也可以继续精简:

  • 替换名为 “rebuilds once” 的测试;它目前只执行了一次读取,无法证明只 rebuild 一次。
  • 将重复的 prompt-composition capture 与 JSON roundtrip fixture 合并为一个端到端 roundtrip 测试,并明确验证名称、类型和字节总量。
  • 删除未使用的 checkpointEvent helper。
  • 优先保留针对以下不变量的聚焦测试:原子提交失败、逆序完成、仅有 provider attempt 的 legacy 数据、request/checkpoint 交错、超过 64 个 tools,以及 diagnostics 成功但 trace 为空或失败。

下面的 inline findings 都是非阻塞的 P2。我会批准这个版本,同时留下具体建议,帮助实现进一步收敛为更小、单一权威的方案。如果现有实现中还有我遗漏的保证或约束,也欢迎直接提出反证或讨论。

AI 辅助审查披露

Codex 负责代码检查和多路独立的对抗性复审。Astro-Han 复核了相关证据,选择并组织了最终 findings,并作出提交本次 review 的最终决定。Astro-Han 没有独立重新执行下面描述的每一个失败序列。

Comment threadpackages/runtime/src/provider-request-telemetry.ts Outdated
Comment threadpackages/storage/src/agent-run-store.ts Outdated
Comment threadpackages/runtime/src/runtime-kernel.ts Outdated
Comment threadpackages/runtime/src/context-diagnostics.ts Outdated
Comment threadpackages/runtime/src/context-diagnostics.ts
Comment threadapps/desktop/src/renderer/session-inspector-overview-model.ts Outdated
Comment threadapps/desktop/src/renderer/session-inspector-panel.tsx
@ARE404

Copy link
Copy Markdown
ContributorAuthor

All seven addressed in 78f9384, threads resolved, CI green. You were right about the root: the sealed record was a second authority, and every finding under it was a symptom.

The snapshot is no longer a record. The completed-main attempt append carries the latest-context row and commits it in its own storage transaction (AgentRunAppendOptions.latestContext). The standalone latest_context_snapshot_recorded event is gone — along with its emitted-type registration, its best-effort queue and callback, the backend/session-manager plumbing that fed it, and its decode path. latest_context is now a projection key nothing is able to append under, which is the property that makes "derived" true rather than merely intended.

On each of the rest:

  • Monotonic projection. Compared on the request's own completedAt with an attemptId tie-break, inside the transaction. Worth reporting: the test you asked for — two completed requests appended in reverse order — failed on the first run, because my guard read orderedAt off a row that never carried it. It had never fired. The invariant only existed once something tried to violate it.
  • Frozen boundary. The compaction boundary now travels with the request (historyCompactBoundary on the tracker) instead of being read back at settlement, so a checkpoint another turn publishes mid-flight can no longer be sealed into a prompt that never saw it. The settlement-time coordinator lookup is deleted.
  • Provider-only legacy ledgers. The cold path falls back to the latest completed provider attempt only when the scan found no canonical record at all, with a test pinning that a canonical record anywhere keeps the fallback out. Old sessions answer again without the fallback becoming an authority for new data.
  • Repair after cold rebuild. It now calls repairEventProjection on the way out, including writing the initialized-empty state when a session has nothing to report. The "rebuilds once" test reads twice against the same real store and asserts the second read scans no run — the claim is proved rather than asserted now.
  • Tool remainder. Desktop adds the Host's already-folded remainingTools to its own hidden rows. It was reporting 59 of 300 tools and silently dropping 236 along with their bytes, while the CLI kept them.
  • Diagnostics behind trace state. Totals and the timeline stay gated by model.empty; the context block is gated by its own content. A successful diagnostics query beside an empty or failed trace renders now.

Tests, per your list: the "rebuilds once" replacement above, a monotonicity case appending in inverse completion order, provider-only legacy data, a canonical-record-present case, and the >64-tool bound reconciling named rows plus remainder against the tool total. The unused checkpointEvent helper is gone.

One thing I did not do: I left the prompt-composition capture and JSON-roundtrip fixtures as two tests rather than merging them. They cover different seams — one that the capture's field names survive the fold, one that they survive JSON storage and decode — and a rename on either side fails a different one. Happy to merge them if you would still rather have a single end-to-end fixture; I did not want to collapse the distinction without saying so.

Verified with the repo's own gates this time — npm run build:test, format:check over all 1405 files, lint, all four typecheck projects — after three rounds where a narrower local check let something through. Runtime suites 50/50; storage's one failure is dugite's git binary missing from an --ignore-scripts install here, not this branch.

@Astro-HanAstro-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 addressing the previous round at the authority boundary. The architectural direction is now much cleaner: the standalone snapshot event is gone, the canonical completed-main attempt is intended to be the only durable authority, and latest_context is a rebuildable projection committed by the same storage transaction. Keeping composition under context.diagnostics.query, outside SessionTrace, is also the right ownership boundary.

I do not think the current head is ready to merge yet, because the remaining failures share one root cause: the implementation distinguishes the session-level projection from the canonical attempt, but it still does not consistently distinguish a send/agent loop from each physical provider request.

That mismatch appears in three places:

  1. The new projection payload is silently dropped by the production adapter chain, so after the first cold repair the warm projection can remain permanently stuck on an older request.
  2. The compaction boundary is modeled as tracker-construction state even though the applicable boundary can change between physical requests within the same send.
  3. The cold rebuild path does not reuse the same authority, eligibility, and ordering rules as the warm projection path.

I recommend a small structural refactor rather than another set of local patches:

  • Replace the optional second argument with one explicit commit object, such as { attempt, latestContext }, across Tracker, AiSdkBackend, the Runtime Host adapter, RuntimeKernel, and AgentRun. This should make dropping part of the commit a type error.
  • Bind composition and the applicable compaction boundary when each physical request is prepared for dispatch, including requests after mid-turn compaction and overflow recovery.
  • Reuse one canonical-record eligibility rule and one (completedAt, attemptId) comparator for both warm updates and cold rebuilds.
  • Give undefined, initialized-empty, and invalid projections distinct behavior instead of allowing null to mean both legacy-uninitialized and confirmed-empty.

I would also use this round to remove the remaining evolution residue:

  • delete the unused checkpointEvent helper;
  • update comments that still describe the deleted latest_context_snapshot_recorded event;
  • merge the overlapping capture→fold, capture→JSON→decode, and hand-written happy-path composition tests into one explicit end-to-end roundtrip;
  • reduce tests that inject a hand-written latestContext directly into storage, and replace them with a production-chain test covering Tracker → Backend → Host adapter → AgentRun → SQLite.

I would not split Runtime, protocol, CLI, and Desktop into separate PRs: they form one vertical contract and should become valid together. The useful reduction is deleting superseded paths and self-proving fixtures, not creating temporarily incomplete cross-layer contracts.

The three inline comments contain the concrete failures and suggested owner-level fixes. Please feel free to push back if an existing production path or lifecycle guarantee changes any of these sequences.

AI-assisted review disclosure

Codex inspected the current head, performed five independent adversarial review passes, and executed an isolated reproduction of the production callback seam confirming that latestContext is dropped by the Runtime Host adapter. I, Astro-Han, reviewed the resulting evidence, selected and prioritized the three root issues, and made the final review decision. The full A→B Runtime Host sequence and the remaining compaction and cold-rebuild interleavings were verified by source tracing rather than executed end to end.

中文对照

感谢作者按照上一轮建议,把实现收敛到了 authority 边界。当前架构方向已经干净很多:独立 snapshot event 已删除,canonical completed-main attempt 被设为唯一 durable authority,latest_context 则成为由同一存储事务提交、可重建的 projection。把 composition 留在 context.diagnostics.query、不再扩张 SessionTrace,职责归属也是正确的。

但我认为当前 head 还不适合合并。剩余问题有同一个根因:实现已经区分了 session-level projection 和 canonical attempt,却仍没有稳定地区分一次 send/agent loop 与其中每一次 physical provider request。

这个生命周期错位体现在三个位置:

  1. 新增的 projection payload 在 production adapter 链路中被静默丢弃,因此第一次 cold repair 后,warm projection 可能永久停留在旧请求。
  2. compaction boundary 被建模成 Tracker 构造状态,但同一次 send 内不同 physical request 实际使用的 boundary 可能变化。
  3. cold rebuild 没有复用 warm projection 路径的 authority、eligibility 和 ordering 规则。

建议做一次小范围结构重构,而不是继续添加局部补丁:

  • 把可选第二参数改成单一 commit object,例如 { attempt, latestContext },并贯穿 Tracker、AiSdkBackend、Runtime Host adapter、RuntimeKernel 和 AgentRun,使中间层丢失其中一部分时成为类型错误。
  • 在每个 physical request 准备 dispatch 时绑定 composition 和实际适用的 compaction boundary,包括 mid-turn compaction 和 overflow recovery 之后的请求。
  • warm update 和 cold rebuild 共用一套 canonical-record eligibility 规则,以及同一个 (completedAt, attemptId) comparator。
  • 明确区分 undefined、initialized-empty 和 invalid projection,不要让 null 同时表达 legacy 未初始化和已确认为空。

这一轮也适合删除剩余的演化残留:

  • 删除未使用的 checkpointEvent helper;
  • 更新仍然描述已删除 latest_context_snapshot_recorded event 的注释;
  • 把 capture→fold、capture→JSON→decode 和手写 happy path 的重叠测试合并为一条明确的端到端 roundtrip;
  • 减少直接向 storage 注入手写 latestContext 的测试,改用一条覆盖 Tracker → Backend → Host adapter → AgentRun → SQLite 的 production-chain 测试。

我不建议把 Runtime、protocol、CLI 和 Desktop 拆成多个 PR;它们构成一个必须共同成立的垂直 contract。真正有价值的缩减,是删除已被替代的路径和自证式 fixture,而不是制造暂时不完整的跨层合同。

三条 inline comments 会说明具体失败和对应 owner 层修法。如果现有 production path 或生命周期保证能够反证这些序列,也欢迎直接提出。

AI 辅助审查披露

Codex 检查了当前 head,进行了五路独立的对抗性复审,并通过隔离复现确认 latestContext 会在 Runtime Host adapter 中被丢弃。Astro-Han 复核了相关证据,选择并排列了三个根因问题,并作出最终 Review 决定。完整的 A→B Runtime Host 流程以及其余 compaction、cold rebuild 交错序列,是通过源码追踪验证的,尚未执行端到端复现。

Comment threadpackages/runtime/src/provider-request-telemetry.ts Outdated
Comment threadpackages/runtime/src/provider-request-telemetry.ts Outdated
Comment threadpackages/runtime/src/context-diagnostics.ts Outdated
@ARE404

Copy link
Copy Markdown
ContributorAuthor

All three addressed in bc61ace, 5cacee, fb3c75e. CI aside, each fix was checked the way the last round taught me to: after the new test passed, I patched the compiled output to remove the fix and re-ran, so every test below is one I have watched fail for its own reason.

1. The boundary now reaches production, per request.historyCompactBoundary is read at each physical dispatch, beside the messages it describes: midTurnState.previousCheckpoint when the turn has that seam — seeded from the pre-turn checkpoint and advanced both by mid-turn capacity compaction and by overflow recovery — and priorReplay.latestHistoryCompactCheckpoint when it does not. One rolling authority rather than two sources reconciled at the end.

The assertion that matters is the difference within one send, which is the only thing a per-tracker value could not also satisfy: three requests, the first two sealing no fold and the third sealing the mid-turn one, in the checkpoint's own numbers. Then the recovery cases — the resend after an overflow reports the fold recovery made for it while the rejected request seals nothing, and its step-0 variant seals pre_turn. Then a checkpoint carried in from an earlier turn, for the branch with no mid-turn seam at all.

While wiring it I made one change you did not ask for. contextBudget.historyCompact.checkpoint is the checkpoint the session holds, not the one the prompt was built from: a loaded checkpoint that misses its prefix or fails the replay fit leaves the raw history in the request, and the replay gates that fall back to the stored-message projection are shaped by no checkpoint at all. applyRuntimeEventContextBudget now returns the checkpoint it actually applied and the boundary is read from there — otherwise the sealed row reports a fold the request never had, which is the failure mode this whole record exists to prevent. A test covers the refused case: the raw prior history is in the prompt, and the row reports no boundary.

2. The cold rebuild follows the same rules.sawCanonicalRecord is tracked apart from anchor, so a ledger whose canonical records are all failed, aborted, a compaction's own call, or undecodable answers "no completed request" instead of promoting a provider row. The comparator is one function in @maka/core used by both writers, and the test for it reads one ledger warm and cold and asserts the two agree on which of two requests that finished in the same millisecond is latest. The projection's three states now get three answers: undefined scans, null is a decided answer that stops the rescan, unreadable falls back to the ledger.

3. Residue. Stale comment gone, the two capture fixtures merged into one capture → JSON → decode → fold roundtrip, and the production-chain test you asked for: a real send through SessionManager with a SQLite run store, read back through readLatestContextDiagnostics, nothing injected — dropping the row at any layer fails it.

One item I did not do as asked: you said to delete the unused checkpointEvent helper. It was unused because the cold path's compaction rebuild had no test at all, so I wrote that test instead — a rebuilt session reports the fold that was in place when its request started, and not a checkpoint written afterwards for a later prompt. Say the word and I will delete both.

@Astro-HanAstro-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 working through the previous rounds so carefully. I reviewed the latest head (fb3c75e) again with three fresh-eye adversarial passes.

The core architecture now looks sound. The canonical completed-main attempt and latest_context projection are committed by the same storage transaction; the request facts travel as one explicit commit object through the production chain; compaction is bound beside each physical provider dispatch, including mid-turn and overflow recovery; and warm updates and cold rebuilds share the same eligibility and ordering rules. I did not find another correctness or authority gap in those paths.

I found one remaining P2 performance/recovery edge, described inline. An unreadable latest_context row triggers a correct ledger rebuild, but the generic repair policy preserves that existing row, so the repaired result cannot replace it. This can leave a long session rescanning its entire ledger on every Inspector refresh. The fix should stay local to projection repair; I would not redesign the surrounding architecture.

This PR also makes a real user-visible UI change: the Inspector gains the estimated-composition section, per-kind and per-tool rows, remainder handling, and new empty states. The PR body currently provides only a textual rendering, and it still describes the superseded trace-based implementation and removed test paths. Please update the body to describe the final context.diagnostics.query / transactional latest_context design and attach a real before/after screenshot. If the previous UI had no corresponding section, a screenshot of the previous location plus the new populated state—or a new screenshot marked “Previously absent”—would be sufficient. This is visual review evidence, not a blocking code finding.

Overall, I am approving this revision. The branch is currently behind main; please update it and let CI run again before merging. The existing focused tests are valuable, and I would stop adding coverage beyond the damaged-projection regression below.

Please feel free to push back if the storage layer has an existing replacement guarantee that changes the damaged-row sequence.

AI-assisted review disclosure

Codex performed the incremental source inspection and three independent fresh-eye adversarial review passes. I, Astro-Han, reviewed the evidence, selected the remaining finding, and made the final review decision.

中文对照

感谢作者耐心处理之前的多轮反馈。我针对最新 head(fb3c75e)又进行了三路 fresh-eye 对抗性复审。

当前核心架构已经成立:canonical completed-main attempt 与 latest_context projection 由同一个存储事务提交;请求事实以单一 commit object 贯穿 production 链路;compaction 在每次物理 provider request dispatch 旁绑定,覆盖 mid-turn 与 overflow recovery;warm update 与 cold rebuild 也共用相同的 eligibility 和排序规则。我没有在这些路径中发现其他正确性或 authority 缺口。

目前只剩一个 P2 级别的性能/恢复边界,详见 inline comment。不可读的 latest_context row 会正确触发 ledger rebuild,但通用 repair policy 会保留已有 row,导致重建结果无法替换损坏数据。长会话可能因此在每次 Inspector 刷新时重新扫描完整 ledger。修复应局限在 projection repair owner 内,不需要重新设计周边架构。

本 PR 也确实带来了用户可见的 UI 变化:Inspector 新增构成估算区块、分类和工具明细、折叠余项以及新的空态。当前 PR body 只有文本化示意,而且仍在描述已经被替代的 trace-based 实现和已删除的测试路径。建议将正文更新为最终的 context.diagnostics.query / transactional latest_context 设计,并补充真实的前后截图。如果旧版没有对应区块,可以展示旧版同一位置与新版 populated 状态,或者给新版截图标注 “Previously absent”。这是供人类确认的视觉证据,不是阻塞性代码 finding。

整体上我会批准当前版本。分支目前落后于 main,请更新后重新运行 CI 再合并。现有聚焦测试都有价值;除下面的 damaged-projection 回归外,我不建议继续增加测试。

如果 storage 层已有能够反证该损坏 row 序列的 replacement guarantee,也欢迎直接提出。

AI 辅助审查披露

Codex 负责本轮增量源码检查和三路独立的 fresh-eye 对抗性复审。Astro-Han 复核了相关证据,选择了最终保留的 finding,并作出 Review 决定。

Comment threadpackages/storage/src/agent-run-store.ts
@ARE404
ARE404force-pushed the are404/feat-prompt-composition branch from fb3c75e to f9c1b6eCompareAugust 13, 2026 03:19
…uild
The generic repair policy preserved any existing projection row for every
key except the checkpoint. `readLatestContextDiagnostics()` deliberately
treats an undecodable `latest_context` row as unanswered and rebuilds
from the canonical ledger — but the repair that followed always kept the
damaged row, and the call passes no `replaceEventId`, so a long session
rescanned its entire ledger on every Inspector refresh.
Repair now uses the same `(completedAt, attemptId)` rule as the
append-time guard, and treats an incumbent whose order cannot be read as
replaceable. A readable row is still never overwritten by an unreadable
one — which is why the regression seeds the damage through
`replaceEventId`: the rule refuses to create that state through the
normal path, so the test says so rather than pretending otherwise.
Verified by removing the branch from the compiled output and re-running:
the new test fails without it and passes with it.
@ARE404

Copy link
Copy Markdown
ContributorAuthor

before
inspector-composition-unrecorded
after
inspector-composition-populated

@ARE404
ARE404force-pushed the are404/feat-prompt-composition branch from 776d6e2 to 3555e7dCompareAugust 13, 2026 08:50
The bar splits the prompt only when the snapshot carries a cache read,
and every provider that caches counts the hits — so a fixture without one
showed the unsplit fallback as if it were the ordinary case. This is also
the state the PR's screenshot documents.
@ARE404
ARE404force-pushed the are404/feat-prompt-composition branch from 3555e7d to cfac1f1CompareAugust 13, 2026 08:55
@ARE404

Copy link
Copy Markdown
ContributorAuthor

Rebased onto b4b4539a7, and everything else you asked for is done: the damaged-projection P2 (c383a065), the PR body rewritten for the final design, and the screenshots posted above. All eleven review threads are resolved.

On the red e2e check — I believe it is a pre-existing flake in quote-selection.spec.ts, not this branch. Reporting the evidence rather than the conclusion, since I got this wrong once already today:

  • main's own CI run 31675601561 (08-13 06:54) fails the same single spece2e/quote-selection.spec.ts:3:1 › a transcript drag releases outside the window through its owning Turn, same 15 passed, same .maka-quote-actions not found. main's next run at 08:46 passed it.
  • This branch touches nothing on that path. The 40 files it changes contain no quote, chat-view or transcript source; the only desktop files outside the Inspector are runtime-host-renderer-operations.ts (+4 lines admitting context.diagnostics.query to the renderer allowlist), its dispatch case (+2), and chat-detail.css (+23, three new rules). All additive — nothing there can remove an element from the DOM.
  • In the failing runs the assertion before it passes: data-e2e-captured-pointer-up is true, so the drag and the capture-routed release both worked. Only the quote layer never mounted.

My first read of this was wrong in a way worth stating: I checked one main run, saw it green, and concluded the branch caused it. One data point cannot separate "my change broke it" from "the spec is flaky" — the 06:54 run is what settles it.

It has now failed twice here against once-passing-once-failing on main, so the reproduction rate may be higher in this configuration than on main; I have not tried to establish why, since the spec is outside this change. If you would rather I did, say so and I will dig rather than leave it at "flaky". Otherwise this needs a rerun with your permissions — mine can only retrigger by pushing, and two pushes have not cleared it.

Everything else in the run is green: typecheck, test_workspaces, test_runtime_host, test, storybook, windows_recovery, windows_baseline.

@Astro-HanAstro-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 the updated head cb94175ed1689a876a5ad803b3cdb3e1f4b7c07d. The remaining damaged latest_context repair edge is fixed at the projection owner, the story-only follow-up does not alter production behavior, and updating onto current main brought in the quote-selection fix. All required checks, including Desktop E2E, are green. I found no remaining P0–P3 issue and approve merging.

@Astro-Han
Astro-Han merged commit 4a7481d into apache:mainAug 13, 2026
10 checks passed
Sign up for freeto 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.

2 participants

@ARE404@Astro-Han