Skip to content

fix(kiro): round-trip the redactedContent reasoning blob - #948

Open
mushikingh wants to merge 5 commits into
lidge-jun:devfrom
mushikingh:fix/kiro-reasoning-roundtrip
Open

fix(kiro): round-trip the redactedContent reasoning blob#948
mushikingh wants to merge 5 commits into
lidge-jun:devfrom
mushikingh:fix/kiro-reasoning-roundtrip

Conversation

@mushikingh

@mushikingh mushikingh commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Problem

Kiro never returns plaintext reasoning for its Sol-family models. reasoningContentEvent carries a KMS-encrypted redactedContent blob, and gpt-5.6-sol's additionalModelRequestFieldsSchema (from ListAvailableModels) accepts only reasoning.effort — there is no display/summary opt-in:

{"modelId":"gpt-5.6-sol",
 "additionalModelRequestFieldsSchema":{"type":"object","properties":{
   "reasoning":{"type":"object","properties":{"effort":{"type":"string",
     "enum":["none","low","medium","high","xhigh","max"],"default":"high"}}}},
   "additionalProperties":false},
 "tokenLimits":{"maxInputTokens":272000,"maxOutputTokens":128000}}

Kiro's own CLI replays that blob on the matching assistant turn to preserve reasoning across turns:

{"assistantResponseMessage":{
  "messageId":"","content":"",
  "reasoningContent":{"redactedContent":"LktUUn5+…"}}}

kiro-events.ts read only reasoningContentEvent.text, which is absent on this wire, so the blob was dropped and never replayed. Every turn restarted without the previous turn's reasoning.

In a long agentic session this degrades badly. The session that prompted this fix ran 123 tool calls at xhigh effort and ended with the model emitting a 322 KB / 162,779-line assistant message of degenerate self-talk (final. ×730, no. ×390, done. ×311, plus lines like "I'm going in circles again") until it was interrupted.

Also fixed

contextUsageEvent was not in KNOWN_EVENT_TYPES, so it was silently ignored. Kiro reports context pressure there; metadataEvent carries only stopReason. The adapter looked for contextUsagePercentage inside metadataEvent, where it never appears, so authoritative context usage never arrived.

Approach

  • Parse redactedContent, and add the missing contextUsageEvent.
  • Carry the blob through the existing ocxr1: envelope as krc, on an envelope-only reasoning item (summary: [], no text deltas) — it round-trips while staying invisible in the app, the same contract the hidden-thinking path already uses. Prefix-based native scrubbing already strips it for non-Kiro backends.
  • Pair it backwards. Kiro emits reasoningContentEvent at the END of a turn, after content and tool calls, so a krc-only item belongs to the assistant turn that already closed. Folding it forward like ordinary reasoning would attach turn N's blob to turn N+1. With no assistant turn to own it, the blob is dropped rather than mis-paired.
  • The blob lives on OcxAssistantMessage.kiroRedactedReasoning, not on a thinking content part, so no other adapter replays provider-private state if the conversation switches providers.

Observed event order per assistant turn (tool-calling turn, kiro-cli 2.14.1):

toolUseEvent ×67 → reasoningContentEvent → metadataEvent → contextUsageEvent → meteringEvent

Verification

Request/response captures from the official kiro-cli at 2.14.1 and 2.16.0 (via KIRO_LOG_LEVEL=trace KIRO_LOG_STDOUT=1), covering a trivial turn, a reasoning-heavy turn, a tool-calling turn, and a resumed multi-turn conversation. Both versions produce identical wire shapes.

  • bun run typecheck — clean
  • bun run test — 7625 pass, 4 fail. All 4 pre-existing on clean upstream/dev, in files this PR does not touch (claude-desktop-cli, cli-restore-back, codex-app-server-processes); confirmed by running the same files on an unmodified worktree — identical failures.
  • bun run privacy:scan — passed
  • New regression tests: kiro-stream (redactedContent capture, text+blob together, contextUsageEvent), kiro-adapter (history replay, and omission when no blob), anthropic-thinking-signature (backwards pairing, and orphan blob dropped).

Not included

meteringEvent is still ignored. Kiro bills in credits, not tokens — there is no tokenUsage on this wire at all, which is why Kiro usage stays estimated. Surfacing credits needs a cost field in OcxUsage plus usage-log/GUI plumbing, so it belongs in its own PR.

Summary by CodeRabbit

  • New Features

    • Preserved and replayed Kiro’s redacted reasoning across conversations, streaming, and batch responses.
    • Added validated context-usage event handling.
    • Retained encrypted reasoning content without displaying it as visible text.
  • Bug Fixes

    • Prevented orphaned redacted reasoning from attaching to unrelated assistant messages.
  • Documentation

    • Documented Kiro reasoning round-tripping, context usage, stop reasons, and credit-based metering.

Kiro never returns plaintext reasoning for its Sol-family models. Its
`reasoningContentEvent` carries a KMS-encrypted `redactedContent` blob, and
`gpt-5.6-sol`'s `additionalModelRequestFieldsSchema` accepts only
`reasoning.effort` — there is no display or summary opt-in. Kiro's own CLI
replays that blob on the matching `assistantResponseMessage.reasoningContent`
to preserve model reasoning across turns.

The adapter read only `reasoningContentEvent.text`, which is absent on this
wire, so the blob was dropped and never replayed. Every turn therefore
restarted without the previous turn's reasoning.

- kiro-events: parse `redactedContent`; add the previously unhandled
  `contextUsageEvent` (Kiro reports context pressure there, not in
  `metadataEvent`, which carries only `stopReason`).
- Carry the blob through the existing `ocxr1:` envelope as `krc` on an
  envelope-only reasoning item, so it round-trips while staying invisible in
  the app — the same contract the hidden-thinking path already uses.
- Pair it backwards: Kiro emits the event at the END of a turn, after content
  and tool calls, so a krc-only item belongs to the assistant turn that already
  closed. Folding it forward would attach turn N's blob to turn N+1. With no
  assistant turn to own it, the blob is dropped rather than mis-paired.
- Replay it on `assistantResponseMessage.reasoningContent`.

Verified against kiro-cli 2.14.1 and 2.16.0 request/response captures.
@github-actions github-actions Bot added the bug Something isn't working label Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d3869cb7-4150-429a-8f10-9a44ad536047

📥 Commits

Reviewing files that changed from the base of the PR and between 3a2f057 and c3d2998.

📒 Files selected for processing (2)
  • src/adapters/kiro.ts
  • tests/kiro-stream.test.ts

📝 Walkthrough

Walkthrough

Kiro event parsing now supports redacted reasoning and authoritative context-usage events. The adapter preserves redacted reasoning across turns. Response bridges encode it in krc envelopes, and the parser replays it onto the preceding assistant message.

Changes

Kiro reasoning round-trip

Layer / File(s) Summary
Kiro event parsing and adapter propagation
src/adapters/kiro-events.ts, src/adapters/kiro.ts, src/types.ts, tests/kiro-adapter.test.ts, tests/kiro-stream.test.ts, structure/04-transports-and-sidecars.md
Kiro parses redacted reasoning and finite context percentages. Assistant turns preserve reasoningContent.redactedContent. Dedicated context usage takes precedence over metadata fallback values.
Reasoning envelope transport and deferred emission
src/bridge.ts, src/responses/reasoning-envelope.ts, tests/kiro-reasoning-roundtrip.test.ts
The bridges retain the latest reasoning blob and emit an envelope-only krc reasoning item after assistant output closes. Budget tracking releases replaced and finalized blobs.
Reasoning replay
src/responses/parser.ts, tests/anthropic-thinking-signature.test.ts
The decoder accepts krc-only envelopes. The parser attaches them to the preceding assistant message and discards orphaned envelopes.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Kiro
  participant KiroEventParser
  participant KiroAdapter
  participant ResponsesBridge
  participant ResponsesParser
  participant AssistantMessage
  Kiro->>KiroEventParser: Send reasoningContentEvent or contextUsageEvent
  KiroEventParser->>KiroAdapter: Emit parsed adapter event
  KiroAdapter->>ResponsesBridge: Forward redacted reasoning data
  ResponsesBridge->>ResponsesParser: Send krc reasoning envelope
  ResponsesParser->>AssistantMessage: Attach krc to preceding assistant turn
Loading

Possibly related PRs

Suggested reviewers: ingwannu, lidge-jun, wibias

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: preserving and replaying Kiro redactedContent reasoning blobs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/bridge.ts`:
- Around line 872-886: Finalize the preceding assistant output before emitting
the Kiro envelope: in src/bridge.ts lines 872-886, close any open currentMsg,
currentReasoning, currentRawReasoning, and tool-call items before allocating and
emitting the envelope-only item; in src/bridge.ts lines 1546-1554, flush
buffered text, summary reasoning, raw reasoning, and tool calls before pushing
it. Add focused streaming and batch regression tests covering text_delta →
kiro_redacted_reasoning → done, and verify parsing attaches
kiroRedactedReasoning to the preceding assistant message.
- Around line 1549-1552: Update the batch reasoning path around
encodeReasoningEnvelope and pushOutput to reserve and commit the encoded
envelope allocation before releasing it via pushOutput, matching the streaming
path’s retained-allocation lifecycle. Ensure pushOutput replaces the retained
allocation with the finalized reasoning item, and add a regression test covering
this behavior under a constrained translator budget.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5e85ae1a-d37b-4719-96ed-64342a48d5fc

📥 Commits

Reviewing files that changed from the base of the PR and between 14b20de and cbb5d21.

📒 Files selected for processing (10)
  • src/adapters/kiro-events.ts
  • src/adapters/kiro.ts
  • src/bridge.ts
  • src/responses/parser.ts
  • src/responses/reasoning-envelope.ts
  • src/types.ts
  • structure/04_transports-and-sidecars.md
  • tests/anthropic-thinking-signature.test.ts
  • tests/kiro-adapter.test.ts
  • tests/kiro-stream.test.ts

Comment thread src/bridge.ts
Comment thread src/bridge.ts Outdated

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cbb5d21ec2

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread src/bridge.ts Outdated
Comment on lines +880 to +881
emit("response.output_item.added", { output_index: outputIndex, item });
emit("response.output_item.done", { output_index: outputIndex, item });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Close active output before adding Kiro reasoning

When a text-only Kiro turn emits visible content followed by redactedContent (the normal order described in this change), the assistant message is still open here, so this branch emits the reasoning item using the same outputIndex as the in-progress message; in the batch sibling branch the same missing flush places the reasoning item before the message, causing parseRequest's backwards attach to treat the blob as orphaned and drop it on the next turn. Flush any open message/raw reasoning/tool item before adding the Kiro envelope, and mirror that in buildResponseJSON.

AGENTS.md reference: src/AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Comment thread src/adapters/kiro.ts
Comment on lines +1193 to +1194
if (ev.redactedContent) {
yield* emitRetained(stage({ type: "kiro_redacted_reasoning", data: ev.redactedContent }));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Delay fallback Kiro blobs until after final answer

When a tool-enabled Kiro request enters text_fallback and the bounded retry returns the private completion tool followed by redactedContent, staging this new event puts it in fallbackEvents; that terminal path yields fallbackEvents before emitting the extracted completionAnswer, so the krc-only reasoning item reaches the bridge before the final assistant message and the parser's backwards pairing drops or mis-pairs it. Hold the Kiro blob until after the extracted final answer (or attach it to that message) so Sol sessions retain reasoning after validated final-answer retries.

AGENTS.md reference: src/AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Comment thread src/bridge.ts Outdated
Comment on lines +1550 to +1552
pushOutput({
type: "reasoning", id: `rs_${uuid()}`, summary: [], encrypted_content: encrypted,
}, bytesOf(encrypted), "reasoning");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Count Kiro blobs in the batch response budget

In the non-streaming path, this branch never charged encrypted before calling pushOutput, so passing bytesOf(encrypted) as replaced bytes immediately releases most of the newly retained item from the reasoning budget. A Kiro parseResponse turn with multiple large redactedContent blobs can therefore stay below the translator cap instead of failing with translation_buffer_limit; pass 0 here (unlike raw/hidden reasoning, there is no prior string buffer to replace).

Useful? React with 👍 / 👎.

CodeRabbit caught a real defect that made the round-trip a no-op in the
streaming path.

Kiro sends its reasoning blob at the END of a turn, while the assistant message
is still open. Emitting the envelope-only item on arrival was wrong twice over:

- `outputIndex` only advances when an item CLOSES, so the open message and the
  envelope item were emitted under the same output index.
- The envelope landed BEFORE the assistant message, and the parser pairs a
  krc-only item backwards, so it found no preceding assistant turn and dropped
  the blob as orphaned — silently defeating the fix.

Both paths now stash the blob and flush it after every open item has closed:
after the closes in the streaming `done` case, and after the trailing flushes in
the batch path. Message phase inference is untouched, so a Kiro final answer is
still classified `final_answer` rather than being force-closed as commentary.

The batch path also released `bytesOf(encrypted)` through `pushOutput` without
ever retaining it. It now charges the blob when stashing and lets `pushOutput`
release that retained allocation, so the translator budget balances.

Adds tests/kiro-reasoning-roundtrip.test.ts, which bridges adapter events and
re-parses the emitted items the way Codex replays history — the end-to-end
coverage the original tests lacked. Three of its five cases fail against the
previous commit.

Scope: verified that gpt-5.6-terra and gpt-5.6-luna return `redactedContent`
exactly like gpt-5.6-sol, so the whole GPT-5.6 family was affected. Handling
keys off the wire field, not the model id.
@mushikingh

Copy link
Copy Markdown
Contributor Author

Both findings validated and fixed in bd13d48.

1. Ordering / output_index collision — confirmed, and worse than described.

outputIndex only advances when an item closes, so an envelope emitted while currentMsg was open reused that index. And because the parser pairs a krc-only item backwards, the envelope landing before the assistant message meant it was dropped as orphaned — the round-trip was a no-op in the streaming path. My original tests missed it: kiro-stream asserts adapter events (pre-bridge) and the parser test hand-built the item order, so nothing exercised bridge → parse.

Rather than closing the open items on arrival, both paths now stash the blob and flush it after every open item has closed — after the closes in the streaming done case, and after the trailing flushes in the batch path. Closing currentMsg early would have forced phase commentary, turning every Kiro final answer into commentary; deferring keeps phase inference intact and yields the [message, reasoning] order the parser expects.

2. Batch budget imbalance — confirmed.

pushOutput released bytesOf(encrypted) that was never retained. Fixed at the source rather than with a reserve/commit round-trip: the blob is now charged when stashed, and pushOutput releases that retained allocation.

Coverage. New tests/kiro-reasoning-roundtrip.test.ts bridges adapter events and re-parses the emitted items the way Codex replays history, asserting distinct output indexes, item order, and that kiroRedactedReasoning reaches the assistant turn — streaming, batch, tool-call-only turns, and budget drift. Three of its five cases fail against cbb5d21, so they are not vacuous.

Scope note. Probing the other models: gpt-5.6-terra and gpt-5.6-luna also return redactedContent and never text, with the same reasoning.effort-only schema — so the whole GPT-5.6 family was affected, not just sol. Claude 4.6+/5 advertise a different contract (thinking.display, output_config.effort); I did not get a reasoning event from claude-sonnet-4.6 at low effort, so I make no claim there. Handling keys off the wire field, not the model id.

Gates: typecheck clean, privacy:scan passed, full suite 7630 pass / 4 fail — the same 4 pre-existing failures on clean upstream/dev, in files this PR does not touch.

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
structure/04_transports-and-sidecars.md (1)

412-414: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the metadataEvent context-usage description.

Lines 412-414 state that metadataEvent carries only stopReason. src/adapters/kiro-events.ts lines 156-172 also accept and propagate a finite contextUsagePercentage from that event.

Describe contextUsageEvent as the authoritative source, but document the metadataEvent percentage as a supported fallback or legacy wire field. This prevents operators from treating a parsed usage value as impossible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@structure/04_transports-and-sidecars.md` around lines 412 - 414, The
metadataEvent description incorrectly says it carries only stopReason; update
the documentation near the Kiro event usage explanation to acknowledge a finite
contextUsagePercentage as a supported fallback or legacy field, while
identifying contextUsageEvent as the authoritative source.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/kiro-reasoning-roundtrip.test.ts`:
- Around line 98-115: Strengthen the regression test around buildResponseJSON so
it verifies retained accounting excludes the raw KRC blob after finalization,
rather than only checking non-negative currentBytes. Use a constrained
TranslatorBudget or an appropriate snapshot/test seam to distinguish the raw
blob allocation from finalized output allocations, and assert the post-build
state contains only the finalized reasoning item’s expected allocation with no
overflow.

---

Outside diff comments:
In `@structure/04_transports-and-sidecars.md`:
- Around line 412-414: The metadataEvent description incorrectly says it carries
only stopReason; update the documentation near the Kiro event usage explanation
to acknowledge a finite contextUsagePercentage as a supported fallback or legacy
field, while identifying contextUsageEvent as the authoritative source.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9253a02a-4dce-4d76-ab7f-897ac94b4e34

📥 Commits

Reviewing files that changed from the base of the PR and between cbb5d21 and bd13d48.

📒 Files selected for processing (4)
  • src/adapters/kiro-events.ts
  • src/bridge.ts
  • structure/04_transports-and-sidecars.md
  • tests/kiro-reasoning-roundtrip.test.ts

Comment thread tests/kiro-reasoning-roundtrip.test.ts Outdated
…blob

The batch budget assertion was vacuous twice over. It passed the budget in the
wrong argument position, so `buildResponseJSON` built its own internal budget and
the snapshot under test was never written to (`highWaterBytes: 0`). Even wired
correctly, asserting `currentBytes >= 0` could not distinguish a leaked raw blob
from the finalized items that legitimately stay retained.

It now uses a 4 KB blob and asserts `currentBytes` equals EXACTLY the finalized
output items' bytes — a still-retained raw blob shows up as ~4 KB of excess, and
releasing bytes that were never charged shows up as a shortfall. A separate
`highWaterBytes` assertion proves the blob was charged while held rather than
never accounted for. Four of the file's five cases now fail against cbb5d21.

Also corrects the metadataEvent description: every capture put the context
percentage in `contextUsageEvent`, but the parser still accepts a finite
`contextUsagePercentage` (and `tokenUsage`) from `metadataEvent` as a fallback,
so documenting it as impossible was wrong.
@mushikingh

Copy link
Copy Markdown
Contributor Author

Both findings valid, fixed in 8610630.

1. Budget regression was vacuous — worse than flagged.

Beyond the weak currentBytes >= 0 assertion, the test passed the budget in the wrong argument position, so buildResponseJSON constructed its own internal budget and the snapshot under test was never written to at all (highWaterBytes: 0). It asserted nothing.

Now uses a 4 KB blob and asserts currentBytes equals exactly the finalized output items' bytes:

  • a still-retained raw blob → ~4 KB of excess
  • releasing bytes never charged (the original defect) → a shortfall

plus highWaterBytes >= finalized + blobLength, proving the blob was charged while held rather than never accounted for. Measured behaviour with the fix: highWater 9650 → current 5650, exactly the finalized items.

Four of the file's five cases now fail against cbb5d21, up from three.

2. metadataEvent doc was wrong.

Correct — kiro-events.ts still accepts a finite contextUsagePercentage (and a tokenUsage block) from metadataEvent, so documenting it as carrying only stopReason would make a legitimately parsed value look impossible. Reworded in both structure/ and the source comment: contextUsageEvent is authoritative and is where every capture put the percentage, with metadataEvent retained as a supported fallback; both feed the same field and any positive value overwrites an earlier one.

Gates: typecheck clean, privacy:scan passed, full suite 7629 pass / 5 fail. Four are the same pre-existing failures as clean upstream/dev; the fifth (storage-policy-job-responsive) is a load-sensitive timing test that passes in isolation on repeat runs and is unrelated to this change.

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@structure/04_transports-and-sidecars.md`:
- Around line 412-417: Update the Kiro context-usage parsing logic around the
contextUsageEvent and metadataEvent assignments to track whether an
authoritative contextUsageEvent value has been received. Accept metadataEvent
values only before that authoritative value, preserve the authoritative value
regardless of event order, and add tests covering both event sequences.

In `@tests/kiro-reasoning-roundtrip.test.ts`:
- Around line 102-121: Wrap the budget setup, response construction, snapshot,
and related assertions in the test around createTranslatorBudget in a
try/finally block, and call budget.dispose() from finally. Preserve all existing
assertions while ensuring disposal occurs even when setup or an assertion fails.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a54f40a6-82cb-4291-be0b-279350a682dd

📥 Commits

Reviewing files that changed from the base of the PR and between bd13d48 and 8610630.

📒 Files selected for processing (3)
  • src/adapters/kiro-events.ts
  • structure/04_transports-and-sidecars.md
  • tests/kiro-reasoning-roundtrip.test.ts

Comment thread structure/04_transports-and-sidecars.md Outdated
Comment thread tests/kiro-reasoning-roundtrip.test.ts Outdated
Both `contextUsageEvent` and `metadataEvent.contextUsagePercentage` assigned the
same variable with last-write-wins, so arrival order alone decided which value
survived and a trailing fallback frame could clobber the authoritative one.
Precedence is now by source: once a contextUsageEvent value lands, later
metadataEvent percentages are ignored. The fallback still works on its own.

Covered in both event orders, with the discriminating values asserted to differ
first so the ordering assertions cannot pass vacuously. The test fails against
the unguarded assignment.

Also disposes the translator budget in the batch regression via try/finally.
`createTranslatorBudget` registers in module-global aggregate accounting, so an
assertion failure would have leaked retained bytes into every later test.
@mushikingh

Copy link
Copy Markdown
Contributor Author

Both findings valid, fixed in 3a2f057.

1. Authoritative context usage could be clobbered by the fallback.

Confirmed: kiro.ts assigned contextUsagePercentage from both context_usage and metadata with last-write-wins, so arrival order alone decided the winner. Precedence is now by source — once a contextUsageEvent value lands, later metadataEvent percentages are ignored; the fallback still works on its own when no authoritative frame arrives.

On the captured wire the order is metadataEvent → contextUsageEvent, so the authoritative value happened to win by luck. This removes the dependence on that.

Covered in both orders. The test first asserts that the 80% and 10% checkpoints are actually distinguishable and that the fallback alone still produces a value, so the ordering assertions cannot pass vacuously; it fails against the unguarded assignment.

I also corrected the structure/ sentence I had added last round — it documented the old last-write-wins behaviour ("any positive value overwrites an earlier one"), which this change makes wrong.

2. Budget disposal.

Valid — createTranslatorBudget registers in module-global aggregate accounting (liveBudgets, aggregateCurrentBytes), and dispose() in a finally is the convention already used in translator-budget.test.ts and cline-pass-provider.test.ts. Without it an assertion failure would have leaked ~5.6 KB of retained bytes into every later test in the process. Wrapped.

Gates: typecheck clean, privacy:scan passed, full suite 7631 pass / 4 fail — the same four pre-existing failures as clean upstream/dev, in files this PR does not touch. (The storage-policy-job-responsive flake noted last round did not recur.)

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/adapters/kiro.ts`:
- Around line 1206-1209: Update the context usage handling in
src/adapters/kiro.ts lines 1206-1209 to accept contextUsagePercentage values >=
0, including zero, and mark contextUsageIsAuthoritative when provided by the
dedicated event. In tests/kiro-stream.test.ts lines 1413-1437, add coverage for
contextUsageEvent(0) followed by a nonzero metadataEvent and assert the result
matches the no-context-percentage baseline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 756f50c7-d253-408d-9b54-e14c25c29273

📥 Commits

Reviewing files that changed from the base of the PR and between 8610630 and 3a2f057.

📒 Files selected for processing (4)
  • src/adapters/kiro.ts
  • structure/04_transports-and-sidecars.md
  • tests/kiro-reasoning-roundtrip.test.ts
  • tests/kiro-stream.test.ts

Comment thread src/adapters/kiro.ts Outdated
@lidge-jun

Copy link
Copy Markdown
Owner

Carried onto the review stack as #953 (stack 3/3), unmodified.

Your commits were taken with git cherry-pick -x, so they keep your authorship — git log --format='%an' on the stack branch shows you, not me. No content was changed; the diff on the stack is byte-identical to what you wrote here, and it applied to dev with no conflict resolution.

Verified on the stack: bun x tsc --noEmit exit 0, and the full suite at 7691 pass / 8 skip / 0 fail across 507 files.

This PR stays open until #953 lands. If a maintainer prefers to take yours directly instead, that path is unaffected — the stack commits get dropped and this one merges. Once #953 merges I'll close this as carried, with the credit already in the commit history rather than in a comment.

Stack: #951 (plan, base dev) → #952 (#908 long-context pricing) → #953 (this carry). Review bottom-up.

Thanks for the fix.

…ading

`contextUsageEvent` claimed authority only when its percentage was > 0, so
precedence depended on the VALUE rather than the source: a genuine 0% reading
(fresh conversation) left contextUsageIsAuthoritative false and a trailing
metadataEvent could then override it with a stale nonzero value.

Accept >= 0 and claim authority; negatives stay rejected as malformed. Nothing
downstream sees a bogus zero-token checkpoint, because contextUsageTotalFloor
already discards a zero floor.

The ordering test now also covers contextUsageEvent(0) followed by a nonzero
metadataEvent, asserting it matches the no-percentage baseline rather than the
fallback's reading. It fails against the > 0 guard.
@mushikingh

Copy link
Copy Markdown
Contributor Author

Valid, fixed in c3d2998.

Confirmed: contextUsageEvent claimed authority only when its percentage was > 0, so precedence depended on the value rather than the source — exactly the coupling the previous round was meant to remove. A genuine 0% reading left contextUsageIsAuthoritative false and a trailing metadataEvent could override it.

Now accepts >= 0 and claims authority. Negatives stay rejected as malformed.

I checked the downstream before widening the guard: contextUsageTotalFloor() already ends in floor > 0 ? floor : undefined, so a 0% reading still yields no checkpoint rather than a bogus zero-token one. The only other consumer is the debug diagnostic, which becomes marginally more accurate. So this is safe to accept.

I did not widen the metadataEvent guard to match. That > 0 is pre-existing fallback behaviour outside this PR's scope, and changing it would alter semantics for wires that do populate it.

Test extended with contextUsageEvent(0) followed by a nonzero metadataEvent, asserting the result matches the no-percentage baseline and specifically differs from the fallback-only reading — it fails against the > 0 guard.

Gates: typecheck clean, privacy:scan passed, full suite 7631 pass / 4 fail — the same four pre-existing failures as clean upstream/dev, in files this PR does not touch.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants