Skip to content

Codex app-server: envelope transcript source (mapper + forward buffer) - #591

Merged
realtonyyoung merged 11 commits into
mainfrom
tonyyoung/ai-1762-envelope-transcript
Aug 19, 2026
Merged

realtonyyoung merged 11 commits into
mainfrom
tonyyoung/ai-1762-envelope-transcript

Conversation

@realtonyyoung

Copy link
Copy Markdown
Collaborator

Second PR of the interactive-hosted-Codex phase (AI-1762) of the app-server epic (AI-1759). Daemon-only; the shipped reviewer path is byte-unchanged because the new envelope emission is gated OFF (§2.5 activates it).

What

Implements the §2.4 envelope transcript for hosted codex app-server sessions — the daemon translates the app-server's JSON-RPC notification stream into the existing AcpEventEnvelope vocabulary, so hosted Codex sessions can be ingested from the protocol stream (like the other ACP vendors) instead of the hooks + kcap watch rollout path.

  • Envelope fields (both repos, additive): Ephemeral/ItemId (the ephemeral live lane + the item key a viewer uses to finalize transient state), a Plan kind (turn/plan/updated full snapshots), and a token_usage kind with additive bucket fields. All default so ContractVersion stays 1 — an older server ignores them. Both AcpEventEnvelopeWireCompatTests pin each other.
  • CodexNotificationMapper — canonical lane = item/completed snapshots + command/mcp tool opens on item/started + turn/plan/updated + per-event token deltas; ephemeral lane = delta notifications accumulated into content-so-far (patchUpdated replaces). Unknown item types surface as a generic tool call and bump a drift counter. Shapes grounded on codex 0.147.0's generated JSON schema; JSON built with Utf8JsonWriter (AOT-safe).
  • CodexUsageDeltaConverter / CodexEphemeralAccumulator — the cumulative→delta usage semantics (attributed to the model-at-instant, correct across model/rerouted) and the per-item cumulative ephemeral content. CodexTokenUsage gains CacheWriteInputTokens (a billed cache-creation bucket that was being dropped).
  • CodexForwardBuffer — the bounded §2.4 buffer: canonical envelopes are never dropped (a full buffer blocks the emit → the read loop stops consuming stdout → the app-server blocks, which is lossless); ephemeral envelopes drop when full; a canonical stall past forwardStallSeconds fires a one-shot terminal fault (never an indefinite wedge, never a silently incomplete transcript).
  • Runtime implements IAcpTranscriptSource and feeds every notification through the mapper into the buffer.

Why gated off

The emission is behind emitEnvelopeTranscript (default false). Feeding the buffer with no attached forwarder would stall it, and draining it without the §2.5 hooks/watch dedup would double-ingest a reviewer session. So this PR ships the transcript surface dormant; §2.5 flips the one flag together with the factory Transcript wiring and the guard-1/2 dedup. A regression test pins the dormant behavior.

Tests

Usage converter (6), ephemeral accumulator (3), notification mapper (15), forward buffer (5, incl. backpressure + stall), runtime integration (gate on and off), both wire-compat suites. Daemon AOT publish clean.

Linear: AI-1762

realtonyyoung and others added 7 commits August 18, 2026 18:26
First self-contained slice of the envelope transcript source. Converts codex
app-server cumulative token-usage snapshots into per-event deltas so the additive
usage pipeline neither double-counts (cumulative → delta) nor mis-attributes
across a model reroute (caller attributes each delta to the model resolved at
that instant). A lower/reset cumulative total contributes the whole new total;
resume supports an exact baseline (thread/read) and a fallback "baseline on next
notification, emit nothing".

6 unit tests. Not yet wired into the runtime — that comes with the envelope
mapper + IAcpTranscriptSource implementation (the rest of PR2, cross-repo:
Ephemeral/ItemId envelope fields + Plan kind land on both kcap-cli and kcap-server).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t lane)

Per-item accumulation of app-server deltas into the cumulative content-so-far
that each ephemeral envelope carries (idempotent replacement at the viewer, no
increment reassembly); Complete drops an item's transient state once its
canonical snapshot is mapped. 3 unit tests. Composes into the mapper (next).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Additive AcpEventEnvelope fields for the codex app-server envelope transcript:
Ephemeral (transient live-lane chunk, no seq, never persisted) and ItemId (the
app-server item id keying which transient state a completed item supersedes),
plus a new Plan envelope kind for turn/plan/updated full snapshots. All
additive/default so ContractVersion stays 1 and an older server is unchanged.
Wire-compat test pins the new snake_case names and the Plan constant against the
kcap-server mirror.
…§2.4)

Adds the additive-billing delta lane distinct from the context-occupancy Usage
kind: a token_usage kind and nullable UsageInputTokens/UsageCachedInputTokens/
UsageCacheWriteInputTokens/UsageOutputTokens/UsageReasoningTokens fields (model
rides the existing Model field). The server stamps these into $usage metadata so
the additive folds count them unchanged. All additive/default so ContractVersion
stays 1. Wire-compat pins the snake_case names + the token_usage constant.
The app-server TokenUsageBreakdown carries cacheWriteInputTokens (cache-creation
tier, billed separately from cached reads); CodexTokenUsage dropped it. Add it to
the record, ParseUsage, and the delta converter's reset/subtraction so no billed
bucket is silently lost before the mapper stamps $usage. Converter tests move to
the 6-bucket shape.
CodexNotificationMapper translates app-server JSON-RPC notifications into the
AcpEventEnvelope vocabulary, composing the ephemeral accumulator + usage delta
converter. Canonical lane: item/completed snapshots (agentMessage/reasoning/
plan/userMessage/command result/fileChange diff/mcp result), command+mcp tool
opens on item/started, turn/plan/updated full snapshots, and per-event token
deltas. Ephemeral lane: agentMessage/reasoning/plan/command/fileChange deltas
accumulate into content-so-far; patchUpdated replaces (snapshot). Unknown item
types surface as a generic tool call and bump a drift counter. Shapes grounded on
codex 0.147.0's generated schema; JSON built with Utf8JsonWriter (AOT-safe).
CodexTokenUsage gains FromTotal/IsZero. 15 tests.
CodexForwardBuffer is the bounded §2.4 buffer: canonical envelopes are never
dropped (a full buffer blocks the emit → the read loop stops consuming stdout →
the app-server blocks, lossless), ephemeral envelopes drop when full, and a
canonical stall past forwardStallSeconds fires a one-shot terminal fault. The
runtime implements IAcpTranscriptSource (thread id / cwd / resolved model /
Envelopes) and feeds every notification through the mapper into the buffer, with
model/rerouted updating the model-at-instant for token attribution.

Emission is gated OFF by default: feeding the buffer with no attached forwarder
would stall it, and draining it without the §2.5 dedup would double-ingest a
reviewer session — so the surface ships dormant and §2.5 flips one flag with the
factory Transcript wiring + dedup guards. Reviewer path byte-unchanged. Buffer +
runtime integration tested (gate on and off); AOT publish clean.
@linear-code

linear-code Bot commented Aug 18, 2026

Copy link
Copy Markdown

AI-1762

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Add gated Codex app-server envelope transcript pipeline

✨ Enhancement 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Adds a default-off Codex JSON-RPC-to-envelope transcript source.
• Converts cumulative usage into model-attributed deltas while preserving cache-write tokens.
• Applies lossless canonical backpressure and droppable cumulative ephemeral updates.
Diagram

graph TD
  A["Codex App Server"] -->|JSON-RPC notifications| B["Hosted Runtime"] -->|gate enabled| C["Notification Mapper"] -->|canonical and ephemeral| D[("Forward Buffer")] -->|next-phase drain| E["Transcript Forwarder"]
  C -->|text deltas| F["Ephemeral Accumulator"]
  C -->|usage snapshots| G["Usage Converter"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Forward directly from the notification loop
  • ➕ Removes the intermediate channel and stall watchdog.
  • ➕ Reduces buffering code and memory usage.
  • ➖ Couples app-server stdout consumption directly to network latency.
  • ➖ Cannot apply separate canonical and ephemeral overflow policies.
  • ➖ Makes forwarding outages harder to bound and diagnose.
2. Use an unbounded envelope channel
  • ➕ Never blocks the notification reader during ordinary forwarding outages.
  • ➕ Simplifies canonical emission.
  • ➖ Allows unbounded memory growth during prolonged outages.
  • ➖ Provides no deterministic failure boundary.
  • ➖ Retains unnecessary ephemeral snapshots under pressure.
3. Store raw JSON-RPC for server-side mapping
  • ➕ Preserves every vendor payload for future remapping.
  • ➕ Centralizes protocol translation on the server.
  • ➖ Expands the server contract and deployment scope.
  • ➖ Couples ingestion to Codex-specific schemas.
  • ➖ Duplicates existing ACP envelope processing paths.

Recommendation: Keep the PR's bounded dual-lane mapper and buffer. It reuses the established ACP vocabulary, preserves canonical transcript integrity, permits safe loss of cumulative ephemeral updates, and bounds forwarding stalls. The default-off gate is appropriate until factory wiring and hooks/watch deduplication land together.

Files changed (12) +1186 / -19

Enhancement (6) +676 / -16
Models.csExtend ACP envelopes for plans, ephemeral state, and token deltas +43/-1

Extend ACP envelopes for plans, ephemeral state, and token deltas

• Adds plan and token-usage event kinds. Extends the version-1 envelope with defaulted ephemeral item identity and additive token bucket fields, preserving backward compatibility.

src/Capacitor.Cli.Core/Models.cs

CodexAppServerHostedAgentRuntime.csExpose the gated Codex envelope transcript source +100/-15

Expose the gated Codex envelope transcript source

• Implements IAcpTranscriptSource, maps notifications into a bounded forward buffer when explicitly enabled, and completes the stream during teardown. It also tracks model reroutes, preserves cache-write usage, and turns prolonged forwarding stalls into terminal runtime faults.

src/Capacitor.Cli.Daemon/Harness/Codex/CodexAppServerHostedAgentRuntime.cs

CodexEphemeralAccumulator.csAccumulate per-item ephemeral content snapshots +37/-0

Accumulate per-item ephemeral content snapshots

• Introduces session-local accumulation of incremental item content into cumulative replacement payloads. Completed items release their transient state.

src/Capacitor.Cli.Daemon/Harness/Codex/CodexEphemeralAccumulator.cs

CodexForwardBuffer.csAdd bounded dual-policy envelope buffering +89/-0

Add bounded dual-policy envelope buffering

• Adds a single-writer channel that blocks canonical events instead of dropping them, while dropping ephemeral events when full. A one-shot timeout callback prevents indefinite canonical stalls.

src/Capacitor.Cli.Daemon/Harness/Codex/CodexForwardBuffer.cs

CodexNotificationMapper.csMap Codex notifications into ACP envelopes +339/-0

Map Codex notifications into ACP envelopes

• Translates completed items, tool starts, plan snapshots, usage updates, and live deltas into canonical or ephemeral ACP envelopes. Unknown item types remain inspectable as generic tool calls and increment a protocol-drift counter.

src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs

CodexUsageDeltaConverter.csConvert cumulative Codex usage into additive deltas +68/-0

Convert cumulative Codex usage into additive deltas

• Computes component-wise token deltas, handles cumulative counter resets, and supports exact or deferred resume baselines. This prevents double counting and enables model-at-instant attribution.

src/Capacitor.Cli.Daemon/Harness/Codex/CodexUsageDeltaConverter.cs

Tests (6) +510 / -3
AcpEventEnvelopeWireCompatTests.csPin the expanded ACP envelope wire contract +53/-1

Pin the expanded ACP envelope wire contract

• Verifies snake-case serialization and round trips for ephemeral fields and every additive token bucket. It also pins the new plan and token-usage kind values and canonical defaults.

test/Capacitor.Cli.Core.Tests.Unit/AcpEventEnvelopeWireCompatTests.cs

CodexAppServerHostedAgentRuntimeTests.csTest runtime envelope gating and integration +45/-2

Test runtime envelope gating and integration

• Confirms enabled runtime notifications reach the forward buffer as model-attributed usage envelopes. A regression test verifies the default-off gate leaves the existing reviewer path dormant.

test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/CodexAppServerHostedAgentRuntimeTests.cs

CodexEphemeralAccumulatorTests.csTest cumulative ephemeral item state +36/-0

Test cumulative ephemeral item state

• Covers cumulative delta folding, independent item buffers, and state removal when an item completes.

test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/CodexEphemeralAccumulatorTests.cs

CodexForwardBufferTests.csTest envelope buffering and stall behavior +96/-0

Test envelope buffering and stall behavior

• Verifies FIFO canonical delivery, ephemeral overflow drops, canonical backpressure, and one-shot terminal faulting after a configured stall timeout.

test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/CodexForwardBufferTests.cs

CodexNotificationMapperTests.csTest Codex notification-to-envelope mappings +210/-0

Test Codex notification-to-envelope mappings

• Covers canonical item snapshots, tool lifecycle events, plan rendering, cumulative ephemeral updates, patch replacement, usage deltas, malformed input, and unknown-type drift handling.

test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/CodexNotificationMapperTests.cs

CodexUsageDeltaConverterTests.csTest cumulative usage delta conversion +70/-0

Test cumulative usage delta conversion

• Validates first snapshots, component deltas, unchanged totals, counter resets, and both resume baseline modes across all token buckets.

test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/CodexUsageDeltaConverterTests.cs

@qodo-code-review

qodo-code-review Bot commented Aug 18, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Reasoning snapshots lose content ✓ Resolved 🐞 Bug ≡ Correctness
Description
RenderReasoning passes the completed item's string arrays to JoinTexts, which only extracts a
text property from object elements. The pinned schema defines both arrays as strings, so canonical
reasoning completions become empty and incorrectly replace their nonempty ephemeral state.
Code

src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs[R241-243]

+    static string RenderReasoning(JsonElement item) {
+        var content = JoinTexts(item.Arr("content"));
+        return content.Length > 0 ? content : JoinTexts(item.Arr("summary"));
Evidence
The mapper routes completed reasoning through RenderReasoning, whose shared JoinTexts helper
reads el.Str("text"). The checked-in Codex schema instead declares both reasoning content and
summary array elements as plain strings, proving those lookups return no content for valid
notifications.

src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs[105-108]
src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs[240-253]
test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/AppServerSchema/codex-app-server-subset.pin.json[1661-1677]
test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/CodexNotificationMapperTests.cs[39-53]

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

## Issue description
Completed reasoning `content` and `summary` are arrays of JSON strings, but the mapper renders them as arrays of objects with a `text` field. Update reasoning rendering to join string elements while retaining the content-over-summary fallback.

## Issue Context
The canonical completion must carry the full authoritative reasoning snapshot because it supersedes ephemeral reasoning state. Adjust the tests to use the pinned schema's actual string-array shape rather than object fixtures.

## Fix Focus Areas
- src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs[105-108]
- src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs[240-253]
- test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/CodexNotificationMapperTests.cs[39-53]
- test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/AppServerSchema/codex-app-server-subset.pin.json[1661-1677]

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


2. Live deltas remain suppressed ✓ Resolved 🐞 Bug ≡ Correctness
Description
Even when envelope emission is enabled, initialization still opts out of the agent-message,
reasoning, command-output, and file-output notifications consumed by the mapper. A compliant
app-server therefore never sends most ephemeral transcript content, so flipping the documented
single gate cannot activate the live lane.
Code

src/Capacitor.Cli.Daemon/Harness/Codex/CodexAppServerHostedAgentRuntime.cs[R400-402]

+        if (_emitEnvelopeTranscript)
+            foreach (var env in _mapper.Map(n.Method, n.Params))
+                _forwardBuffer.Emit(env);
Evidence
The runtime's existing DeltaOptOut list contains the five live notification methods and is
unconditionally sent during initialization, while the pinned schema explicitly defines these names
as notifications to suppress. The newly added mapper path depends on those exact methods, and the
fake server only records opt-outs rather than enforcing them, leaving the integration test unable to
detect the failure.

src/Capacitor.Cli.Daemon/Harness/Codex/CodexAppServerHostedAgentRuntime.cs[67-73]
src/Capacitor.Cli.Daemon/Harness/Codex/CodexAppServerHostedAgentRuntime.cs[245-251]
src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs[72-81]
test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/AppServerSchema/codex-app-server-subset.pin.json[793-801]
test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/FakeCodexAppServer.cs[101-107]

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

## Issue description
The envelope path maps live delta notifications, but `initialize` always lists those same methods in `optOutNotificationMethods`. When `emitEnvelopeTranscript` is enabled, omit the mapper-required methods from the opt-out list so the app-server actually sends them.

## Issue Context
The pinned protocol schema defines opt-outs as exact notification suppression. Preserve the current low-volume behavior while the feature gate is off, but make initialization conditional when the envelope transcript is enabled and add an integration test whose fake honors suppression.

## Fix Focus Areas
- src/Capacitor.Cli.Daemon/Harness/Codex/CodexAppServerHostedAgentRuntime.cs[67-73]
- src/Capacitor.Cli.Daemon/Harness/Codex/CodexAppServerHostedAgentRuntime.cs[245-251]
- src/Capacitor.Cli.Daemon/Harness/Codex/CodexAppServerHostedAgentRuntime.cs[396-402]
- test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/FakeCodexAppServer.cs[101-107]
- test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/CodexAppServerHostedAgentRuntimeTests.cs[76-93]

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


3. Comment restates envelope defaults ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new test comment narrates the exact Ephemeral and ItemId defaults asserted immediately below
it. It adds no non-obvious rationale and violates the requirement that comments not restate code.
Code

test/Capacitor.Cli.Core.Tests.Unit/AcpEventEnvelopeWireCompatTests.cs[R83-84]

+        // A canonical envelope leaves the ephemeral lane fields at their defaults — Ephemeral=false so
+        // the server sequences/persists it as today, and no ItemId unless the mapper stamps one.
Evidence
Rule 8 prohibits comments that restate obvious behavior. The comment says the fields default to
Ephemeral=false and no ItemId, and the following assertions verify those same values verbatim.

CLAUDE.md: Comments must be concise and explain the non-obvious “why” (not restate code)
test/Capacitor.Cli.Core.Tests.Unit/AcpEventEnvelopeWireCompatTests.cs[82-88]

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

## Issue description
A test comment restates the default values directly expressed by the construction and assertions that follow.

## Issue Context
The test name and assertions already document that canonical envelopes default to `Ephemeral=false` and `ItemId=null`.

## Fix Focus Areas
- test/Capacitor.Cli.Core.Tests.Unit/AcpEventEnvelopeWireCompatTests.cs[82-88]

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


View high (1)
4. McpArguments bypasses JSON helpers ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The new mapper directly checks JsonElement.ValueKind in McpArguments, TryContent, and
IsMcpError, despite equivalent JsonElementExtensions APIs being available. This introduces the
manual JSON-kind inspection prohibited by the checklist.
Code

src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs[R311-312]

+    static string? McpArguments(JsonElement item) =>
+        item.TryGetProperty("arguments", out var a) && a.ValueKind == JsonValueKind.Object ? a.GetRawText() : null;
Evidence
Rule 7 requires JSON inspection to use JsonElementExtensions. The mapper directly compares
ValueKind for object, null, undefined, and string handling, while the shared extensions expose
equivalent IsObject, IsString, IsNull, and Obj APIs.

CLAUDE.md: Use JsonElementExtensions instead of directly checking JSON ValueKind
src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs[309-330]
src/Capacitor.Cli.Core/JsonElementExtensions.cs[11-18]
src/Capacitor.Cli.Core/JsonElementExtensions.cs[35-37]

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

## Issue description
The Codex notification mapper directly checks `JsonElement.ValueKind` instead of using the repository's `JsonElementExtensions` APIs.

## Issue Context
`IsObject`, `IsString`, `IsNull`, and named-property accessors such as `Obj` provide the approved equivalents for these checks.

## Fix Focus Areas
- src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs[309-330]
- test/Capacitor.Cli.Daemon.Tests.Unit/Harness/Codex/CodexNotificationMapperTests.cs[88-90]

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


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 src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs Outdated
Comment thread test/Capacitor.Cli.Core.Tests.Unit/AcpEventEnvelopeWireCompatTests.cs Outdated
Comment thread src/Capacitor.Cli.Daemon/Harness/Codex/CodexNotificationMapper.cs Outdated
…rop)

- RenderMcpResult prefers error over result so a failed mcp call carrying both a
  result and an error renders the error, staying consistent with ToolIsError
  (Copilot finding 2). Regression test added.
- CodexForwardBuffer swallows the shutdown-cancellation OperationCanceledException
  explicitly instead of propagating it out of the read-loop notification handler;
  the stall-timeout path is unchanged (finding 3).

Declined with rationale: fileChange→ToolCall is the §2.4 spec mapping (lone-ToolCall
rendering is a §2.6 server concern, finding 1); IsReset any-component-decrease is
correct for monotonic cumulative buckets (finding 4); positional CodexTokenUsage
insertion is safe — verified zero positional construction sites (all use FromTotal
or named args).
@realtonyyoung

Copy link
Copy Markdown
Collaborator Author

Copilot code-review flow — complete

Copilot (context-only, cross-repo) returned 5 findings. Outcome:

Addressed (commit 7e52e05):

  • F2 (mcp error precedence): RenderMcpResult now prefers error over result, so a failed mcp call carrying both renders the error and stays consistent with ToolIsError. Regression test added.
  • F3 (shutdown OCE): CodexForwardBuffer swallows the shutdown-cancellation OperationCanceledException explicitly rather than propagating it out of the read-loop notification handler; the stall-timeout fault path is unchanged.

Declined, with rationale:

  • F1 (fileChange → ToolCall): this is the §2.4 spec mapping ("tool call with diff content"); how a lone diff-bearing tool call renders as complete is a §2.6 server-side concern, not a daemon mapping defect.
  • F4 (IsReset any-vs-all): any-component-decrease is correct — the buckets are monotonic cumulative per-thread counters, so a single decrease genuinely means a thread reset/rebaseline.
  • F5 / positional-field risk: informational; verified there are zero positional CodexTokenUsage construction sites (all use FromTotal or named args), so the CacheWriteInputTokens insertion is safe.

Copilot also explicitly confirmed clean: the _ephemeral.Complete placement, the single-writer buffer contract, ephemeral drop-on-full + one-shot stall guard, the two resume-baseline modes, model/rerouted ordering, and DisposeAsync completing the channel.

…xtensions)

Two real bugs fixed:
- Reasoning content/summary are arrays of plain STRINGS (pinned schema), not
  objects with a text field — RenderReasoning read .text off each element and
  produced an EMPTY canonical reasoning envelope that wrongly superseded the
  nonempty ephemeral state. New JoinStrings joins the string elements; JoinTexts
  stays for userMessage (UserInput objects). Test fixtures corrected to string arrays.
- initialize always opted out of the agent-message/reasoning/command/file delta
  notifications, so flipping emitEnvelopeTranscript alone could never activate the
  ephemeral lane (the app-server never sent them). The opt-out is now empty when
  the transcript is on, unchanged (perf) when off. Test asserts the empty opt-out.

Rule fixes:
- McpArguments/RenderMcpResult/IsMcpError drop raw JsonElement.ValueKind checks for
  the JsonElementExtensions accessors (.Obj/.Str/.Arr) per the repo checklist.
- Drop a wire-compat test comment that restated the asserted defaults.
…esult

A completed fileChange emitted a lone ToolCall with no matching ToolResult, an
asymmetry both Copilot and Kiro flagged (a consumer expecting ToolCall→ToolResult
pairing would see an orphan). It now emits a paired ToolCall (carrying the diff) +
ToolResult (the apply status, error on failed/declined), mirroring commandExecution
and mcp. Tests cover the pair and the failed-apply error flag.

Declined with rationale: the read-loop backpressure block is the intentional §2.4
lossless-stall design (bounded by the configurable watchdog); model/rerouted needs
no usage-baseline reset because thread/tokenUsage/updated.total is thread-cumulative
(monotonic across reroutes) so the model-at-instant attribution is correct and a
reset would drop the spanning interval; JoinTexts already handles userMessage's
UserInput objects (distinct from reasoning's string arrays).
@realtonyyoung

Copy link
Copy Markdown
Collaborator Author

Kiro code-review flow — complete (third vendor)

Ran a third-vendor Kiro review (context-only) asking it to find what Copilot and qodo missed. 5 findings:

Addressed (commit 456c3c8):

  • fileChange orphan tool call (MEDIUM — flagged by Kiro and Copilot): a completed fileChange emitted a lone ToolCall with no matching ToolResult. It now emits a paired ToolCall (carrying the diff) + ToolResult (apply status, error on failed/declined), mirroring commandExecution/mcp so no consumer sees an orphan. Tests cover the pair + the failed-apply error flag.
  • Shared-itemId ephemeral note (LOW): added a clarifying comment that patchUpdated deliberately bypasses the accumulator.

Declined, with rationale:

  • Read-loop backpressure block (HIGH): this is the intentional §2.4 lossless-stall design — when the read loop blocks, the app-server blocks on stdout and sends nothing (so no turn/completed is missed), bounded by the configurable stall watchdog. Not a bug.
  • model/rerouted usage baseline (MEDIUM): no reset needed — thread/tokenUsage/updated.total is thread-cumulative (monotonic across reroutes), so the model-at-instant attribution is correct; a reset would actually drop the reroute-spanning interval's usage.
  • JoinTexts for userMessage (LOW): unfounded — JoinTexts exists and correctly handles userMessage's UserInput objects (distinct from reasoning's string arrays, which use JoinStrings).

Three vendors (Copilot + qodo + Kiro) have now reviewed; all actionable findings are addressed.

@realtonyyoung
realtonyyoung merged commit 432f8ea into main Aug 19, 2026
6 checks passed
@realtonyyoung
realtonyyoung deleted the tonyyoung/ai-1762-envelope-transcript branch August 19, 2026 01:46
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.

1 participant