Skip to content

feat(agent-runtime): support multiple images per turn#472

Merged
yacosta738 merged 7 commits into
mainfrom
feat/multi-image-per-turn-ingestion
Apr 10, 2026
Merged

feat(agent-runtime): support multiple images per turn#472
yacosta738 merged 7 commits into
mainfrom
feat/multi-image-per-turn-ingestion

Conversation

@yacosta738
Copy link
Copy Markdown
Contributor

Related Issues

Closes #331
Refs #266


Summary

  • add configurable multi-image turn support in the Rust runtime with a default limit of 4 images per turn
  • preserve ordered multi-image staging and provider dispatch, and upgrade image ingress observability to report full turn metadata
  • sync and archive the OpenSpec change for multi-image-per-turn ingestion

Tested Information

  • cargo fmt --manifest-path clients/agent-runtime/Cargo.toml --all -- --check
  • cargo clippy --manifest-path clients/agent-runtime/Cargo.toml --all-targets -- -D warnings
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml multimodal_max_images_per_turn
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml process_admits_four_images_and_preserves_provider_order
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml process_rejects_when_image_count_exceeds_configured_limit
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml process_rejects_five_images_when_default_limit_is_omitted
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml stage_channel_images_cleans_up_partial_staging_on_failure
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml process_emits_provider_error_with_full_multi_image_metadata
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml build_multimodal_api_messages_preserves_all_images_in_order
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml image_blocks_preserve_multiple_images_in_order
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml build_gemini_contents_preserves_all_images_in_order
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml image_ingress_multi_image_turn_uses_turn_level_fields
  • cargo test --manifest-path clients/agent-runtime/Cargo.toml image_ingress_rejected_turn_can_include_effective_limit

Documentation Impact

  • Docs updated in:
  • openspec/specs/channel-image-ingestion/spec.md
  • openspec/specs/runtime-image-pipeline/spec.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/
  • No docs update required because:
  • I verified the documentation matches the current behavior.

Breaking Changes

  • None.

Checklist

  • I have checked that there isn’t already a PR solving the same problem.
  • I have read the Contributing Guidelines.
  • I ensured my code follows the project's style guidelines.
  • I have added or updated tests that prove my fix is effective or that my feature works.
  • I have updated the documentation, or I explained above why no documentation update is needed.
  • I verified the documentation matches the current behavior.
  • I have documented any breaking changes in the Breaking Changes section.
  • I have linked the related issue (if any).

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 10, 2026

Warning

Rate limit exceeded

@yacosta738 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 57 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 57 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 647a49d7-3c77-47ae-9bc8-bb057ee2b07b

📥 Commits

Reviewing files that changed from the base of the PR and between b4eedf7 and 90d5225.

📒 Files selected for processing (2)
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md
  • openspec/specs/channel-image-ingestion/spec.md
📝 Walkthrough

Walkthrough

Replaces the hardcoded single-image assumption with configurable multi-image support: adds multimodal.max_images_per_turn (optional, default 4, ceiling 8), updates gating/staging to validate and preserve ordered Vec<StagedImage>, extends observability to emit per-image metadata and aggregate bytes, and adds provider/regression tests and docs.

Changes

Cohort / File(s) Summary
Constants & Validation
clients/agent-runtime/src/channels/media.rs, clients/agent-runtime/src/config/schema.rs
Removed MAX_IMAGES_PER_TURN=1; added DEFAULT_MAX_IMAGES_PER_TURN=4 and MAX_IMAGES_PER_TURN_CEILING=8. validate_image_count now accepts max_images_per_turn. MultimodalConfig gains max_images_per_turn: Option<usize> and effective_max_images_per_turn() with startup validation.
Channel Orchestration & Staging
clients/agent-runtime/src/channels/mod.rs
gate_and_stage_images derives effective max_images_per_turn from config and uses it for validation and rejection messages; emit_image_ingress/emit_image_provider_outcome emit multi-image metadata; staging dispatch refactored into ChannelImageFetcher, cleans up partially staged temp files on per-image failures; tests added for ordering, metadata, cleanup, and rejection semantics.
Observability Schema & Logging
clients/agent-runtime/src/observability/traits.rs, clients/agent-runtime/src/observability/log.rs, clients/agent-runtime/src/observability/mod.rs
Added ImageIngressImageMeta (mime_type, byte_len). ImageIngressEvent extended with max_images_per_turn: Option<usize>, images: Vec<ImageIngressImageMeta>, and total_byte_len. Logging updated to emit these fields; tests/fixtures updated accordingly.
Provider Integration Tests
clients/agent-runtime/src/providers/anthropic.rs, clients/agent-runtime/src/providers/compatible.rs, clients/agent-runtime/src/providers/gemini.rs
Added tests ensuring providers receive all staged images in order and preserve MIME/base64 encoding across Anthropic, OpenAI-compatible, and Gemini builders.
Docs / Specs / Verification
openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/*, openspec/specs/channel-image-ingestion/spec.md, openspec/specs/runtime-image-pipeline/spec.md
New design/proposal/specs/tasks/verify artifacts documenting default=4, ceiling=8, whole-turn rejection for over-limit, turn-level observability, ordering guarantees, and required regression coverage.
Minor
clients/agent-runtime/src/gateway/mod.rs
Formatting tweak in handle_chat_stream legacy path; no behavioral change.

Sequence Diagram

sequenceDiagram
    participant Config as Config Validator
    participant Gate as Image Gate
    participant Stage as Stage/Emit
    participant Observe as Observer
    participant Provider as Provider

    Note over Config: Startup: validate\nmax_images_per_turn ∈ [1,8]\ndefault=4 if omitted

    rect rgba(100, 150, 200, 0.5)
        Note over Gate,Provider: Per-turn flow

        Gate->>Gate: Check image_count ≤ effective_max
        alt Over limit
            Gate->>Observe: Emit rejection (attempted_count, effective_limit, images=[])
            Observe-->>Gate: TooManyImages
        else Within limit
            Gate->>Stage: Stage all images in order
            Stage->>Observe: Emit ingress (max_images_per_turn, images[], total_byte_len)
            Observe-->>Stage: ✓
            Stage->>Provider: Dispatch [StagedImage] slice (ordered)
            Provider->>Provider: Attach all images in order
            Provider-->>Stage: ✓
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

area:rust, area:docs, risk:high, risk:security

Suggested reviewers

  • yuniel-acosta
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(agent-runtime): support multiple images per turn' follows Conventional Commits style with a feat prefix and clear scope, is well under 72 characters (53 total), and accurately describes the main feature change.
Description check ✅ Passed The PR description covers all required template sections: related issues (#331, #266), clear summary of changes, comprehensive tested information with specific test commands, documentation impact listing affected files, breaking changes clearly stated as none, and completed checklist items.
Linked Issues check ✅ Passed All objectives from #331 are met: MAX_IMAGES_PER_TURN is now configurable with default 4, provider payloads handle multiple images preserving order, observability events report full turn metadata, and comprehensive multi-image tests are included.
Out of Scope Changes check ✅ Passed All changes are scoped to multi-image per-turn support: media constants/validation, channel gating/staging, observability events, config schema, provider tests, and OpenSpec documentation. One formatting-only change in gateway/mod.rs is inconsequential.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/multi-image-per-turn-ingestion

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Apr 10, 2026

Deploying corvus with  Cloudflare Pages  Cloudflare Pages

Latest commit: 90d5225
Status: ✅  Deploy successful!
Preview URL: https://cc30121d.corvus-42x.pages.dev
Branch Preview URL: https://feat-multi-image-per-turn-in.corvus-42x.pages.dev

View logs

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 10, 2026

✅ Contributor Report

User: @yacosta738
Status: Passed (12/13 metrics passed)

Metric Description Value Threshold Status
PR Merge Rate PRs merged vs closed 90% >= 30%
Repo Quality Repos with ≥100 stars 0 >= 0
Positive Reactions Positive reactions received 10 >= 1
Negative Reactions Negative reactions received 0 <= 5
Account Age GitHub account age 3086 days >= 30 days
Activity Consistency Regular activity over time 108% >= 0%
Issue Engagement Issues with community engagement 0 >= 0
Code Reviews Code reviews given to others 539 >= 0
Merger Diversity Unique maintainers who merged PRs 2 >= 0
Repo History Merge Rate Merge rate in this repo 91% >= 0%
Repo History Min PRs Previous PRs in this repo 220 >= 0
Profile Completeness Profile richness (bio, followers) 90 >= 0
Suspicious Patterns Spam-like activity detection 1 N/A

Contributor Report evaluates based on public GitHub activity. Analysis period: 2025-04-10 to 2026-04-10

Change the Ok(response) match arm in clients/agent-runtime/src/gateway/mod.rs to an expression-style arm and reformat its arguments across multiple lines for improved readability. No functional behavior was changed.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

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

⚠️ Outside diff range comments (1)
clients/agent-runtime/src/observability/traits.rs (1)

561-593: ⚠️ Potential issue | 🟡 Minor

Align these fixtures with the emitted single-vs-multi-image contract.

Line 567 builds a 2-image event but still populates mime_type/byte_len, while Line 632 builds a 1-image provider error and leaves them empty. clients/agent-runtime/src/channels/mod.rs:259-286 only emits the top-level pair when there is exactly one staged image. These tests currently admit states the runtime never produces, so they weaken regression coverage around the new turn-level fields.

Proposed fix
@@
         let event = ImageIngressEvent {
             channel: "telegram".into(),
             provider: Some("gemini".into()),
             model: Some("gemini-2.0-flash".into()),
             outcome: ImageIngressOutcome::Admitted,
             reason: None,
-            image_count: 2,
+            image_count: 1,
             max_images_per_turn: None,
-            images: vec![
-                ImageIngressImageMeta {
-                    mime_type: "image/jpeg".into(),
-                    byte_len: 204_800,
-                },
-                ImageIngressImageMeta {
-                    mime_type: "image/png".into(),
-                    byte_len: 102_400,
-                },
-            ],
-            total_byte_len: Some(307_200),
+            images: vec![ImageIngressImageMeta {
+                mime_type: "image/jpeg".into(),
+                byte_len: 204_800,
+            }],
+            total_byte_len: Some(204_800),
             mime_type: Some("image/jpeg".into()),
             byte_len: Some(204_800),
         };
@@
-        assert_eq!(event.image_count, 2);
-        assert_eq!(event.images.len(), 2);
-        assert_eq!(event.images[1].mime_type, "image/png");
-        assert_eq!(event.total_byte_len, Some(307_200));
+        assert_eq!(event.image_count, 1);
+        assert_eq!(event.images.len(), 1);
+        assert_eq!(event.images[0].mime_type, "image/jpeg");
+        assert_eq!(event.total_byte_len, Some(204_800));
         assert_eq!(event.mime_type.as_deref(), Some("image/jpeg"));
         assert_eq!(event.byte_len, Some(204_800));
@@
         let event = ImageIngressEvent {
             channel: "telegram".into(),
             provider: None,
             model: None,
             outcome: ImageIngressOutcome::ProviderError,
             reason: Some(ImageIngressReason::ProviderError),
             image_count: 1,
             max_images_per_turn: None,
             images: vec![ImageIngressImageMeta {
                 mime_type: "image/webp".into(),
                 byte_len: 333,
             }],
             total_byte_len: Some(333),
-            mime_type: None,
-            byte_len: None,
+            mime_type: Some("image/webp".into()),
+            byte_len: Some(333),
         };

Also applies to: 626-645

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/observability/traits.rs` around lines 561 - 593,
The test fixtures for ImageIngressEvent violate the runtime contract that
top-level mime_type and byte_len are present only when exactly one image is
staged: update the 2-image fixture (the ImageIngressEvent with image_count = 2
and two entries in images) to set mime_type and byte_len to None, and update the
1-image provider-error fixture to populate top-level mime_type and byte_len from
the single ImageIngressImageMeta; locate and change the construction and
assertions around ImageIngressEvent, ImageIngressImageMeta, image_count,
mime_type and byte_len so assertions reflect that multi-image events have None
for top-level mime_type/byte_len while single-image events expose them.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/agent-runtime/src/channels/mod.rs`:
- Around line 4831-4887: The test
process_admits_four_images_and_preserves_provider_order currently only checks
counts so it won't detect reordering; modify the ImageAwareProvider stub
(provider_impl) to record a stable per-image identifier (e.g., part id or a
generated token from make_image_part) for each image received instead of only
incrementing image_counts, then update the test to assert the provider_impl's
recorded image sequence equals ["img-a","img-b","img-c","img-d"]; locate usages
around RecordingObserver/RecordingChannel and make_image_part to capture the id
in the provider's load/send handler and add an assertion that the outbound image
order is preserved.
- Around line 1655-1679: The per-image match repeatedly calls
build_telegram_channel/build_whatsapp_channel/build_discord_channel/build_slack_channel
inside the loop, causing repeated config/token cloning and client recreation;
instead, construct the appropriate channel fetcher once before the per-image
loop (using the same match on msg.channel.as_str() to produce a single fetcher
or boxed trait object that implements fetch_and_stage_image) and then call
fetch_and_stage_image(channel_handle, declared_mime.as_deref(), max_bytes).await
on that single fetcher for each image, ensuring you propagate the same
ok_or(media::ImageRejectionReason::FetchFailed)? logic at construction time so
the loop only performs the fetch calls and not builder work or heavy cloning.
- Around line 1618-1631: The emit_image_ingress call is currently passing None
for the turn-level max_images_per_turn, so admitted/provider outcome events lack
the new field; update the call site in mod.rs (the emit_image_ingress
invocation) to pass the effective max images limit (the same value used by
reject_image_turn) instead of None — e.g., replace the None argument with the
effective limit variable (such as route.max_images_per_turn or the same computed
value reject_image_turn uses) so emit_image_ingress receives and emits
max_images_per_turn.

In `@clients/agent-runtime/src/observability/log.rs`:
- Around line 510-517: The test fixture sets image_count: 2 but only provides
one ImageIngressImageMeta in the images vec and a single-image total_byte_len;
update the fixture used in log.rs so the images vector contains two
ImageIngressImageMeta entries (e.g., add a second image with appropriate
mime_type and byte_len), adjust total_byte_len to be the sum of both byte_len
values, and ensure mime_type/max_images_per_turn fields reflect the multi-image
scenario; change the entries near the image_count, images, total_byte_len,
mime_type, and max_images_per_turn fields so the aggregates and counts are
consistent.

In
`@openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md`:
- Around line 9-19: Add a single blank line immediately after the "### In Scope"
and "### Out of Scope" subheadings so each heading is followed by an empty line
per markdown conventions; update the proposal.md content around the "### In
Scope" and "### Out of Scope" headings to insert the blank lines (preserve the
existing list items and spacing elsewhere).

In
`@openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md`:
- Around line 1-6: Replace the existing second-level heading "## Verification
Report" with a top-level heading "# Verification Report" at the top of the
document (the section that currently shows Change:
multi-image-per-turn-ingestion) and ensure the file ends with a single trailing
newline (add a newline after the final line).

In `@openspec/specs/channel-image-ingestion/spec.md`:
- Around line 278-297: REQ-11 (Regression Coverage for Multi-Image Channel
Ingestion) is placed before REQ-10 (Deduplication), causing non-sequential
numbering; fix by either swapping the two requirement blocks so REQ-10 precedes
REQ-11 or renumbering the headers so they read sequentially (e.g., change REQ-11
→ REQ-10 and REQ-10 → REQ-11) and update any internal references to those
requirement IDs in the document to match the new ordering.

---

Outside diff comments:
In `@clients/agent-runtime/src/observability/traits.rs`:
- Around line 561-593: The test fixtures for ImageIngressEvent violate the
runtime contract that top-level mime_type and byte_len are present only when
exactly one image is staged: update the 2-image fixture (the ImageIngressEvent
with image_count = 2 and two entries in images) to set mime_type and byte_len to
None, and update the 1-image provider-error fixture to populate top-level
mime_type and byte_len from the single ImageIngressImageMeta; locate and change
the construction and assertions around ImageIngressEvent, ImageIngressImageMeta,
image_count, mime_type and byte_len so assertions reflect that multi-image
events have None for top-level mime_type/byte_len while single-image events
expose them.
🪄 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

Run ID: 9580fabb-3c97-4747-9c01-872a5beb504d

📥 Commits

Reviewing files that changed from the base of the PR and between 3ca2396 and 0c00c2d.

📒 Files selected for processing (18)
  • clients/agent-runtime/src/channels/media.rs
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/providers/anthropic.rs
  • clients/agent-runtime/src/providers/compatible.rs
  • clients/agent-runtime/src/providers/gemini.rs
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/design.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/specs/channel-image-ingestion/spec.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/specs/runtime-image-pipeline/spec.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/state.yaml
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/tasks.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md
  • openspec/specs/channel-image-ingestion/spec.md
  • openspec/specs/runtime-image-pipeline/spec.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: pr-checks
  • GitHub Check: sonar
  • GitHub Check: submit-gradle
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (8)
**/*

⚙️ CodeRabbit configuration file

**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.

Files:

  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/state.yaml
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/providers/gemini.rs
  • clients/agent-runtime/src/providers/compatible.rs
  • clients/agent-runtime/src/providers/anthropic.rs
  • clients/agent-runtime/src/observability/mod.rs
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/tasks.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/specs/channel-image-ingestion/spec.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/design.md
  • openspec/specs/channel-image-ingestion/spec.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md
  • clients/agent-runtime/src/channels/media.rs
  • openspec/specs/runtime-image-pipeline/spec.md
  • clients/agent-runtime/src/config/schema.rs
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/specs/runtime-image-pipeline/spec.md
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/channels/mod.rs
clients/agent-runtime/src/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/**/*.rs: Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements
Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Files:

  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/providers/gemini.rs
  • clients/agent-runtime/src/providers/compatible.rs
  • clients/agent-runtime/src/providers/anthropic.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/channels/media.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/channels/mod.rs
clients/agent-runtime/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Run cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, and cargo test for code validation, or document which checks were skipped and why

Files:

  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/providers/gemini.rs
  • clients/agent-runtime/src/providers/compatible.rs
  • clients/agent-runtime/src/providers/anthropic.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/channels/media.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/channels/mod.rs
**/*.rs

⚙️ CodeRabbit configuration file

**/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness.
Flag unnecessary clones, unchecked panics in production paths, and weak error context.
Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.

Files:

  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/providers/gemini.rs
  • clients/agent-runtime/src/providers/compatible.rs
  • clients/agent-runtime/src/providers/anthropic.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/channels/media.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/channels/mod.rs
clients/agent-runtime/src/providers/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Implement Provider trait in src/providers/ and register in src/providers/mod.rs factory when adding a new provider

Files:

  • clients/agent-runtime/src/providers/gemini.rs
  • clients/agent-runtime/src/providers/compatible.rs
  • clients/agent-runtime/src/providers/anthropic.rs
**/*.{md,mdx}

⚙️ CodeRabbit configuration file

**/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.
For user-facing docs, check EN/ES parity or explicitly note pending translation gaps.

Files:

  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/tasks.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/specs/channel-image-ingestion/spec.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/design.md
  • openspec/specs/channel-image-ingestion/spec.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md
  • openspec/specs/runtime-image-pipeline/spec.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/specs/runtime-image-pipeline/spec.md
clients/agent-runtime/src/channels/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Implement Channel trait in src/channels/ with consistent send, listen, and health_check semantics and cover auth/allowlist/health behavior with tests

Files:

  • clients/agent-runtime/src/channels/media.rs
  • clients/agent-runtime/src/channels/mod.rs
clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Files:

  • clients/agent-runtime/src/config/schema.rs
🧠 Learnings (4)
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/*.rs : Run `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` for code validation, or document which checks were skipped and why

Applied to files:

  • clients/agent-runtime/src/providers/gemini.rs
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/Cargo.toml : Do not add heavy dependencies for minor convenience; justify new crate additions

Applied to files:

  • clients/agent-runtime/src/observability/mod.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/channels/**/*.rs : Implement `Channel` trait in `src/channels/` with consistent `send`, `listen`, and `health_check` semantics and cover auth/allowlist/health behavior with tests

Applied to files:

  • clients/agent-runtime/src/channels/mod.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/providers/**/*.rs : Implement `Provider` trait in `src/providers/` and register in `src/providers/mod.rs` factory when adding a new provider

Applied to files:

  • clients/agent-runtime/src/channels/mod.rs
🪛 LanguageTool
openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/tasks.md

[style] ~34-~34: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...later image in the same turn fails. - Verify observability keeps mime_type/`byte_l...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/design.md

[style] ~99-~99: This adverb was used twice in the sentence. Consider removing one of them or replacing them with a synonym.
Context: ... reaping - Construct StagedImageGuard only after the full staging loop and ignore ...

(ADVERB_REPETITION_PREMIUM)

🪛 markdownlint-cli2 (0.22.0)
openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md

[warning] 9-9: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 16-16: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md

[warning] 1-1: First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)


[warning] 122-122: Files should end with a single newline character

(MD047, single-trailing-newline)

🔇 Additional comments (22)
clients/agent-runtime/src/providers/compatible.rs (1)

1872-1925: Strong regression test for ordered multi-image serialization.

This test correctly verifies both image order preservation and per-image base64 payload mapping in the built content blocks.

clients/agent-runtime/src/providers/anthropic.rs (1)

1439-1499: Good coverage for Anthropic multi-image ordering.

The assertions validate correct ordering and per-image encoding across mixed MIME types on the last user turn.

clients/agent-runtime/src/providers/gemini.rs (1)

921-977: Gemini multi-image ordering test looks solid.

This is a good end-to-end builder-level check that preserves image order and byte fidelity for inline_data.

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/specs/runtime-image-pipeline/spec.md (1)

1-145: Spec update is comprehensive and aligned with the runtime change.

The archived requirements clearly capture ordered full-slice dispatch, configurable limits, deterministic over-limit behavior, and turn-level observability.

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/state.yaml (1)

1-12: Archive state metadata looks correct.

Phase progression and terminal archive state are consistent with this change lifecycle.

clients/agent-runtime/src/observability/mod.rs (1)

21-22: Re-export update is correct.

Adding ImageIngressImageMeta here keeps the observability public surface aligned with the updated event schema.

clients/agent-runtime/src/observability/log.rs (1)

239-241: Image ingress logging now correctly reflects multi-image turn metadata.

Good update to include limit context and aggregate image metadata in the emitted event.

clients/agent-runtime/src/channels/media.rs (3)

14-18: LGTM on the new constants.

Clear documentation and sensible defaults. The ceiling of 8 provides operator flexibility while preventing unbounded payload growth.


376-385: LGTM on the config-aware validation helper.

The updated signature allows the effective limit to flow from config, removing the hardcoded single-image assumption. The > comparison correctly admits counts equal to the limit.


623-640: Test coverage for boundary conditions is solid.

Tests exercise both the default (4) and ceiling (8) limits, including the rejection edge cases (5 and 9).

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/tasks.md (1)

1-35: Task list is comprehensive and aligns with the implementation.

All phases are well-structured covering config, gating, observability, provider regressions, and verification. The incomplete task 5.2 is correctly marked as archive-only.

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md (1)

46-70: Spec compliance matrix is thorough.

The 20/20 scenario coverage with explicit test mappings provides good traceability between requirements and verification.

openspec/specs/channel-image-ingestion/spec.md (2)

63-74: Clear specification of multi-image staging semantics.

The requirements for preserving turn association and channel order are well-defined.


109-139: Deterministic rejection behavior is well-specified.

The requirement that over-limit turns are rejected as a whole (no partial admission) with user-visible error reporting both count and limit provides clear implementation guidance.

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md (1)

39-49: Risk mitigation strategies are sound.

Provider adapter regression tests, additive observability fields, and bounded config with ceiling address the identified risks appropriately.

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/specs/channel-image-ingestion/spec.md (1)

1-111: Delta spec accurately captures the behavioral changes.

The "Previously" annotations clearly document what changed from the MVP single-image behavior, and the scenarios align with the updated main spec.

openspec/specs/runtime-image-pipeline/spec.md (3)

57-75: Provider dispatch contract clearly mandates full slice preservation.

The requirement that provider dispatch "MUST NOT collapse a multi-image turn to only the first staged image" directly addresses the MVP limitation.


200-248: Whole-turn rejection semantics are well-defined.

The requirement "MUST NOT partially dispatch any subset of their staged images" ensures deterministic behavior and aligns with the channel-ingestion spec.


490-531: Multi-image observability and regression coverage requirements are complete.

REQ-9 and REQ-10 ensure turn-level metadata fidelity and regression coverage for the new multi-image behavior.

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/design.md (3)

54-89: Observability schema evolution is well-designed.

The decision to keep mime_type/byte_len only for single-image turns while adding images[] and total_byte_len for multi-image turns provides backward compatibility without misleading "first image wins" behavior.


91-104: Partial staging cleanup decision addresses the multi-image failure surface.

With multi-image turns, mid-loop failures become a normal case. Best-effort cleanup inside stage_channel_images() keeps temp file growth bounded without changing the overall RAII contract.


228-238: Testing strategy is comprehensive.

Coverage spans config validation, count validation boundaries, gate-time rejection, partial staging cleanup, provider ordering, and single-image compatibility.

Comment thread clients/agent-runtime/src/channels/mod.rs
Comment thread clients/agent-runtime/src/channels/mod.rs Outdated
Comment thread clients/agent-runtime/src/channels/mod.rs
Comment thread clients/agent-runtime/src/observability/log.rs
Comment thread openspec/specs/channel-image-ingestion/spec.md
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
openspec/specs/channel-image-ingestion/spec.md (1)

60-74: ⚠️ Potential issue | 🟠 Major

The validation order here no longer matches the runtime.

gate_and_stage_images() rejects over-limit turns before stage_channel_images() fetches or validates any files. Specifying count validation after MIME/size would require unnecessary downloads and conflicts with the deterministic whole-turn rejection behavior described later in this document.

As per coding guidelines, **/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openspec/specs/channel-image-ingestion/spec.md` around lines 60 - 74, The
spec's validation order is incorrect: runtime first enforces image-count limits
in gate_and_stage_images() and rejects whole turns over
multimodal.max_images_per_turn before stage_channel_images() fetches/validates
files. Update the doc to match runtime by moving the count check (effective
default 4 when multimodal.max_images_per_turn is omitted) to step 4a (first) so
gate_and_stage_images() behavior is accurately described; keep MIME magic-byte
sniffing and MAX_IMAGE_BYTES size checks as subsequent steps performed during
stage_channel_images() while preserving staging details (StagedImage, SHA-256
naming, InlineBytes, channel_origin) and the requirement that admitted images
per user turn remain ordered and associated with that turn.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/agent-runtime/src/channels/mod.rs`:
- Around line 1690-1705: The runtime now treats Slack as an implemented
image-ingestion channel (see ChannelImageFetcher::Slack and
build_slack_channel), but the channel-image-ingestion spec (spec.md) still marks
Slack as planned/contract-only; either update the spec to document Slack's
staging path and contract details to match the runtime, or remove/guard the
Slack branch in the dispatcher so runtime behavior matches the documented
contract; ensure the fix touches the dispatcher branch using
ChannelImageFetcher::Slack and the channel-image-ingestion spec (spec.md) so
code and docs remain synchronized.

In
`@openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md`:
- Around line 8-118: The markdown in verify-report.md jumps from H1 to H3 (e.g.,
"### Completeness", "### Build & Tests Execution"), causing MD001; fix by
promoting those H3 headings to H2 (change "###" to "##" for sections like
Completeness, Build & Tests Execution, Spec Compliance Matrix, Correctness,
Coherence, Issues Found, Verdict) or insert a single H2 placeholder under the H1
and keep sub-sections consistent so the document uses a proper H1→H2→H3
hierarchy throughout.

In `@openspec/specs/channel-image-ingestion/spec.md`:
- Around line 258-276: The spec currently requires rejected multi-image turns to
"describe every image in the turn", which conflicts with the implementation in
reject_image_turn() that emits attempted image_count and max_images_per_turn but
leaves images, total_byte_len, mime_type, and byte_len empty/None; update the
spec text and the two rejection scenarios so they require reporting outcome
`Rejected`, reason `TooManyImages`, and that observability MUST report the
attempted image_count and effective max_images_per_turn while MAY NOT include
per-image metadata or raw bytes (images, total_byte_len, mime_type, byte_len may
be omitted/None), referencing the rejection behavior implemented in
reject_image_turn().

---

Outside diff comments:
In `@openspec/specs/channel-image-ingestion/spec.md`:
- Around line 60-74: The spec's validation order is incorrect: runtime first
enforces image-count limits in gate_and_stage_images() and rejects whole turns
over multimodal.max_images_per_turn before stage_channel_images()
fetches/validates files. Update the doc to match runtime by moving the count
check (effective default 4 when multimodal.max_images_per_turn is omitted) to
step 4a (first) so gate_and_stage_images() behavior is accurately described;
keep MIME magic-byte sniffing and MAX_IMAGE_BYTES size checks as subsequent
steps performed during stage_channel_images() while preserving staging details
(StagedImage, SHA-256 naming, InlineBytes, channel_origin) and the requirement
that admitted images per user turn remain ordered and associated with that turn.
🪄 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

Run ID: c14528dd-9453-4eb5-9396-8dc131beb3f9

📥 Commits

Reviewing files that changed from the base of the PR and between 0c00c2d and b4eedf7.

📒 Files selected for processing (7)
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/observability/traits.rs
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md
  • openspec/specs/channel-image-ingestion/spec.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: sonar
  • GitHub Check: pr-checks
  • GitHub Check: submit-gradle
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (8)
clients/agent-runtime/src/{security,gateway,tools}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Treat src/security/, src/gateway/, src/tools/ as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Files:

  • clients/agent-runtime/src/gateway/mod.rs
clients/agent-runtime/src/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/**/*.rs: Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements
Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Files:

  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/channels/mod.rs
clients/agent-runtime/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Run cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, and cargo test for code validation, or document which checks were skipped and why

Files:

  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/channels/mod.rs
clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Files:

  • clients/agent-runtime/src/gateway/mod.rs
**/*.rs

⚙️ CodeRabbit configuration file

**/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness.
Flag unnecessary clones, unchecked panics in production paths, and weak error context.
Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.

Files:

  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/channels/mod.rs
**/*

⚙️ CodeRabbit configuration file

**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.

Files:

  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/channels/mod.rs
  • openspec/specs/channel-image-ingestion/spec.md
**/*.{md,mdx}

⚙️ CodeRabbit configuration file

**/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.
For user-facing docs, check EN/ES parity or explicitly note pending translation gaps.

Files:

  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md
  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md
  • openspec/specs/channel-image-ingestion/spec.md
clients/agent-runtime/src/channels/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Implement Channel trait in src/channels/ with consistent send, listen, and health_check semantics and cover auth/allowlist/health behavior with tests

Files:

  • clients/agent-runtime/src/channels/mod.rs
🧠 Learnings (7)
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths

Applied to files:

  • clients/agent-runtime/src/gateway/mod.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/channels/**/*.rs : Implement `Channel` trait in `src/channels/` with consistent `send`, `listen`, and `health_check` semantics and cover auth/allowlist/health behavior with tests

Applied to files:

  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/channels/mod.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/*.rs : Run `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` for code validation, or document which checks were skipped and why

Applied to files:

  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Inspect existing module and adjacent tests before editing; define scope boundary with one concern per PR and avoid mixed feature+refactor+infra patches

Applied to files:

  • openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/**/*.rs : Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Applied to files:

  • clients/agent-runtime/src/channels/mod.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Keep startup path lean and avoid heavy initialization in command parsing flow

Applied to files:

  • clients/agent-runtime/src/channels/mod.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/providers/**/*.rs : Implement `Provider` trait in `src/providers/` and register in `src/providers/mod.rs` factory when adding a new provider

Applied to files:

  • clients/agent-runtime/src/channels/mod.rs
🪛 markdownlint-cli2 (0.22.0)
openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/verify-report.md

[warning] 8-8: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🔇 Additional comments (6)
clients/agent-runtime/src/gateway/mod.rs (1)

2154-2157: Looks good — formatting-only change with no behavior drift.

This preserves existing sanitization and success-path semantics in handle_chat_stream.

clients/agent-runtime/src/observability/log.rs (2)

239-241: Good additive multi-image telemetry logging.

Lines 239-241 correctly log turn-level limit, ordered image metadata, and aggregate bytes without raw payload content.


511-524: Fixture now matches multi-image aggregates.

The admitted ingress fixture is consistent (image_count, two images entries, and total_byte_len sum), which strengthens regression confidence.

clients/agent-runtime/src/observability/traits.rs (2)

54-70: Event schema extension is clean and backward-compatible.

Adding images + total_byte_len + max_images_per_turn while retaining legacy single-image fields provides a safe migration path for observers.


807-862: Strong targeted coverage for multi-image turn semantics.

These tests validate ordered per-image metadata and rejected-turn effective limits, which directly protects the new contract.

openspec/changes/archive/2026-04-10-multi-image-per-turn-ingestion/proposal.md (1)

1-64: Proposal scope and risk framing look solid.

The document stays focused on the multi-image ingestion concern and keeps implementation, observability, and rollback expectations aligned.

Comment thread clients/agent-runtime/src/channels/mod.rs
Comment thread openspec/specs/channel-image-ingestion/spec.md Outdated
@sonarqubecloud
Copy link
Copy Markdown

@yacosta738 yacosta738 merged commit 5afd58b into main Apr 10, 2026
13 checks passed
@yacosta738 yacosta738 deleted the feat/multi-image-per-turn-ingestion branch April 10, 2026 10:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: multi-image per turn support

1 participant