Skip to content

feat(agent-runtime): add typed slash command contract#546

Merged
yacosta738 merged 6 commits into
developfrom
feature/dallay-540-slash-command-context
Apr 16, 2026
Merged

feat(agent-runtime): add typed slash command contract#546
yacosta738 merged 6 commits into
developfrom
feature/dallay-540-slash-command-context

Conversation

@yacosta738
Copy link
Copy Markdown
Contributor

Related Issues

Closes #540
Refs #527
Refs #541


Summary

This PR adds the typed slash command execution contract for clients/agent-runtime.

It introduces a richer CommandContext, typed slash command requirement metadata, and a non-lossy internal slash command outcome model while preserving the existing external transport envelopes.

It also tightens /resume caller-scope enforcement through the memory boundary, updates the ingress adapters to carry the typed contract end to end, and syncs the completed OpenSpec artifacts for the change.


Tested Information

Validated with the Rust checks relevant to this change:

  • cargo fmt --all -- --check
  • cargo clippy --manifest-path clients/agent-runtime/Cargo.toml --all-targets -- -D warnings
  • targeted webhook preview compatibility regression coverage during verify
  • targeted session command / SQLite caller-scope validation during verify

Reviewers may want to focus on:

  • session_commands/types.rs
  • session_commands/service.rs
  • pre_execution/mod.rs
  • ingress adapter changes in gateway, webhook, CLI, and channels

Documentation Impact

  • Docs updated in:
    • openspec/specs/slash-command-registry/spec.md
    • openspec/specs/sessions/spec.md
    • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/
  • No docs update required because:
    • User-facing product docs were not required for this internal runtime contract change.
  • 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).

…vice.rs issues

- gateway/mod.rs: replace MockMemory with SqliteMemory in slash-session tests
  - canonical_outcome_early_response_intercepts_slash_session_commands
  - legacy_webhook_preview_intercepts_slash_session_commands
  - web_chat_stream_returns_deterministic_slash_session_sse_without_provider_execution
  - update assertions to expect 422 + session_command_failed error

- session_commands/service.rs:
  - FakeMemory::get_session now looks up by session_id
  - build_resume_context adds truncation limits (2k per entry, 16k global)
  - visibility check uses get_session instead of paginated list

- openspec/specs/sessions/spec.md: add trailing newline
# Conflicts:
#	clients/agent-runtime/src/gateway/mod.rs
#	clients/agent-runtime/src/session_commands/service.rs
@cloudflare-workers-and-pages
Copy link
Copy Markdown

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

Deploying corvus with  Cloudflare Pages  Cloudflare Pages

Latest commit: 08f33f0
Status: ✅  Deploy successful!
Preview URL: https://28b16acf.corvus-42x.pages.dev
Branch Preview URL: https://feature-dallay-540-slash-com.corvus-42x.pages.dev

View logs

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 15, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Caller-scoped session resumption (tighter resume ownership) and slash-commands handled in plan mode as well as standard mode.
  • Bug Fixes

    • Non-lossy structured command outcomes (Success/Failure) with typed failure kinds; /resume now enforces caller-scope authorization and clearer failure/status responses.
  • Refactor

    • Unified, owned command execution context passed across CLI/HTTP/stream/webhook/channel for consistent identity and execution-mode semantics.
  • Documentation

    • Spec and design docs added describing the typed command-context and outcome contract.

Walkthrough

This PR introduces a typed, owned CommandContext and a non-lossy SessionCommandOutcome contract across ingress and session-command execution, converts string-tagged requirement metadata to typed enums, threads caller-scoped resumable-session authorization into the memory trait (get_resumable_session_for_scope), and updates transports/registry/service/sqlite to use the new types and flows.

Changes

Cohort / File(s) Summary
Trait & Types
clients/agent-runtime/crates/corvus-traits/src/memory.rs, clients/agent-runtime/src/session_commands/types.rs
Added get_resumable_session_for_scope to Memory; ResumableSessionEntry derives PartialEq/Eq. Replaced borrowed CommandContext<'_> + string tags with an owned CommandContext and typed enums (CommandCapability, CommandPermission, CommandBackend, CommandCaller, CommandSessionSource, CommandIngressSource). Introduced SessionCommandOutcome::{Success,Failure} and typed failure kinds.
Ingress API & Dispatch
clients/agent-runtime/src/pre_execution/mod.rs
Changed evaluate_ingress to accept CommandContext and return IngressDecision::SessionCommand { outcome } carrying SessionCommandOutcome; removed synthetic result mapping and simplified dispatch pass-through.
Transport Adapters
clients/agent-runtime/src/gateway/..., clients/agent-runtime/src/gateway/webhook_dispatch.rs, clients/agent-runtime/src/channels/mod.rs, clients/agent-runtime/src/main.rs
Transports now build CommandContext::for_* (including execution mode and session source), call evaluate_ingress(context, prompt), and map SessionCommandOutcome (success.message / failure.message) to transport responses (HTTP status, SSE events, CLI prints). Adjusted early interception/idempotency ordering for preview/legacy paths.
Registry & Handlers
clients/agent-runtime/src/session_commands/registry.rs, clients/agent-runtime/src/session_commands/service.rs, clients/agent-runtime/src/session_commands/mod.rs
Registry dispatch now takes owned CommandContext and returns Option<SessionCommandOutcome>. Built-in handlers return SessionCommandOutcome; error handling converted to SessionCommandFailure/kinds and command requirements use typed slices. Public re-exports expanded to include new types; legacy result/error types removed.
SQLite Memory
clients/agent-runtime/src/memory/sqlite.rs
Implemented get_resumable_session_for_scope(session_id, caller_scope_key) to return a ResumableSessionEntry only when token/caller-scope matches; added tests validating scope enforcement.
Command Requirements & Registry Metadata
clients/agent-runtime/src/session_commands/types.rs, clients/agent-runtime/src/session_commands/registry.rs
SlashCommandRequirements now uses typed slices (capabilities, permissions, backends) instead of string tag vectors; registry registrations updated to use typed requirement constants.
Docs & Specs
openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/*, openspec/specs/*
Added design/proposal/spec/task/verify docs describing the typed command-context, permission model, non-lossy outcome contract, and caller-scoped /resume authorization; updated session/registry specs accordingly.

Sequence Diagram(s)

sequenceDiagram
    participant Transport as Transport (CLI/HTTP/SSE/Channel)
    participant PreExec as pre_execution::evaluate_ingress
    participant Registry as SlashCommandRegistry / Service
    participant Memory as Memory (SqliteMemory)

    Transport->>PreExec: build CommandContext (for_*)
    PreExec->>Registry: dispatch(context, prompt)
    alt SessionCommandOutcome::Success
        Registry-->>PreExec: SessionCommandOutcome::Success { message, data }
        PreExec-->>Transport: map success.message -> response
    else SessionCommandOutcome::Failure
        Registry-->>PreExec: SessionCommandOutcome::Failure { kind, message }
        alt failure requires scoped resume lookup
            Registry->>Memory: get_resumable_session_for_scope(session_id, caller_scope_key)
            Memory-->>Registry: Option<ResumableSessionEntry>
        end
        PreExec-->>Transport: map failure.message -> response (status or error envelope)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • yuniel-acosta
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title follows Conventional Commits style with 'feat' prefix, includes scope, and clearly summarizes the main change—adding a typed slash command contract.
Description check ✅ Passed Description covers all required sections: related issues, summary of changes, tested information, documentation impact, breaking changes, and completed checklist.
Linked Issues check ✅ Passed PR fulfills issue #540 acceptance criteria: introduces CommandContext with session/caller/source/mode/capability state, typed requirement metadata, non-lossy outcome model, tightened /resume scope enforcement, and adds corresponding tests.
Out of Scope Changes check ✅ Passed All changes align with PR objectives: CommandContext/outcome types in session_commands, ingress adapter wiring, memory layer scoped query, pre_execution seam updates, and OpenSpec artifact synchronization. No transport envelope changes or unrelated refactoring.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/dallay-540-slash-command-context

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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 15, 2026

✅ Contributor Report

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

Metric Description Value Threshold Status
PR Merge Rate PRs merged vs closed 89% >= 30%
Repo Quality Repos with ≥100 stars 0 >= 0
Positive Reactions Positive reactions received 11 >= 1
Negative Reactions Negative reactions received 0 <= 5
Account Age GitHub account age 3092 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 561 >= 0
Merger Diversity Unique maintainers who merged PRs 2 >= 0
Repo History Merge Rate Merge rate in this repo 92% >= 0%
Repo History Min PRs Previous PRs in this repo 246 >= 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-16 to 2026-04-16

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: 9

Caution

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

⚠️ Outside diff range comments (2)
clients/agent-runtime/src/gateway/mod.rs (1)

1975-1998: ⚠️ Potential issue | 🟠 Major

Reserve idempotency before the preview slash-command short-circuit.

Line 1975 can return before Line 1990 records X-Idempotency-Key, so preview-mode /webhook slash commands bypass duplicate suppression entirely. A retried request with the same key will execute again instead of returning the duplicate envelope.

♻️ Suggested ordering
-    if is_preview && !dispatcher_enabled {
-        if let Some((response, persist_idempotency)) = canonical_outcome_early_response(
-            &state,
-            &session_id,
-            &scrubbed_message,
-            token_hash.as_deref(),
-        )
-        .await
-        {
-            release_idempotency_key(&state, None, persist_idempotency);
-            return response;
-        }
-    }
-
     let reserved_idempotency_key = if let Some(idempotency_key) = webhook_idempotency_key(&headers)
     {
         if !state.idempotency_store.record_if_new(idempotency_key) {
             return webhook_duplicate_response(idempotency_key);
         }
         Some(idempotency_key)
     } else {
         None
     };
+
+    if is_preview && !dispatcher_enabled {
+        if let Some((response, persist_idempotency)) = canonical_outcome_early_response(
+            &state,
+            &session_id,
+            &scrubbed_message,
+            token_hash.as_deref(),
+        )
+        .await
+        {
+            release_idempotency_key(&state, reserved_idempotency_key, persist_idempotency);
+            return response;
+        }
+    }
As per coding guidelines, "`**/*`: Security first, performance second. Validate input boundaries, auth/authz implications, and secret management. Look for behavioral regressions, missing tests, and contract breaks across modules."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/gateway/mod.rs` around lines 1975 - 1998, Move the
idempotency reservation before the preview-mode short-circuit so preview
slash-command requests still go through duplicate suppression: call
webhook_idempotency_key(&headers) and record_if_new(...) on
state.idempotency_store before checking is_preview && !dispatcher_enabled; on
duplicate return webhook_duplicate_response(idempotency_key) as before; if you
reserved a key and then still short-circuit via
canonical_outcome_early_response(...).await, ensure you call
release_idempotency_key(&state, None, persist_idempotency) or
release_idempotency_key(&state, None, reserved_idempotency_key) as appropriate
to avoid leaking reservations; preserve the existing persist_idempotency
handling from canonical_outcome_early_response.
clients/agent-runtime/src/session_commands/service.rs (1)

576-589: ⚠️ Potential issue | 🟠 Major

Bug: Resume context truncation realignment logic is incorrect.

The realignment check at line 582 will never be true. After find("\n- ") returns position pos, skip(pos) produces a string starting with "\n- ", not "- ".

// Current: never matches because adjustment starts with "\n"
if adjustment.starts_with("- ") {
    context = adjustment;
}
🐛 Proposed fix
     if context.chars().count() > MAX_CONTEXT_LENGTH {
         let excess = context.chars().count() - MAX_CONTEXT_LENGTH;
         context = context.chars().skip(excess).collect::<String>();
-        // Ensure we don't start mid-entry
         if let Some(pos) = context.find("\n- ") {
-            let adjustment: String = context.chars().skip(pos).collect();
-            if adjustment.starts_with("- ") {
-                context = adjustment;
-            }
+            // Skip past the newline to start at the entry
+            context = context.chars().skip(pos + 1).collect();
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/session_commands/service.rs` around lines 576 -
589, The truncation realignment in the function that enforces MAX_CONTEXT_LENGTH
uses context.find("\n- ") and then creates adjustment via
context.chars().skip(pos) which yields a string starting with "\n- ", so the
conditional adjustment.starts_with("- ") never matches; update the logic in that
block (where context is reassigned after computing excess and pos) to either
skip one more char (pos + 1) or check for adjustment.starts_with("\n- ") and
then strip the leading newline before assigning to context so list entries
beginning with "- " are correctly realigned.
🤖 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 1870-1876: The CommandContext is being constructed with a
hardcoded ExecutionMode::Standard; change the third argument in
CommandContext::for_channel to use the actual runtime execution mode (the
existing ExecutionMode value in scope, e.g., execution_mode or runtime_mode)
instead of ExecutionMode::Standard so slash commands inherit the real plan vs
standard mode; if no ExecutionMode variable is available in this function,
thread an ExecutionMode parameter through the caller chain into this site
(affecting ingress_context creation), update references to
crate::session_commands::CommandContext::for_channel accordingly, and add/adjust
tests to cover both plan and standard paths to prevent authz/behavior
regressions.

In `@clients/agent-runtime/src/gateway/mod.rs`:
- Around line 6168-6177: Add a regression test for the failure-path of the new
stream contract: when the session command outcome is
SessionCommandOutcome::Failure, assert that the response status is
StatusCode::UNPROCESSABLE_ENTITY (422) and that the collected response body (the
body_str built from resp.into_body().collect()) contains the SSE error envelope
("event: error") and the expected failure message instead of the success
chunks/done lines; update or add a test near the existing success-case
assertions that sets up a Failure outcome and verifies resp.status() ==
StatusCode::UNPROCESSABLE_ENTITY and body_str.contains("event: error") plus the
failure text.
- Around line 1770-1775: The HTTP command context is always being created with
CommandSessionSource::Explicit, losing the real source resolved by
resolve_session_id; update the two handle_webhook() call sites so they pass the
resolved session_source (the value returned from resolve_session_id) into
crate::session_commands::CommandContext::for_gateway_http instead of the
hardcoded CommandSessionSource::Explicit, ensuring the context uses the actual
session source for /webhook, /resume and other source-sensitive flows.

In `@clients/agent-runtime/src/gateway/webhook_dispatch.rs`:
- Around line 396-410: evaluate_ingress is being called with
request.execution_mode before the server clamps the mode via
resolve_webhook_execution_mode, allowing pre-execution to observe a less
restrictive mode; change the call site so you first compute the clamped mode
using resolve_webhook_execution_mode(...) and pass that clamped value into
CommandContext::for_webhook (or otherwise into evaluate_ingress) instead of
request.execution_mode so evaluate_ingress (and ingress_context) always sees the
final, enforced execution mode; update references to
ingress_context/request.execution_mode/evaluate_ingress accordingly.

In `@clients/agent-runtime/src/main.rs`:
- Around line 1401-1405: The fast-return path that handles slash commands via
maybe_handle_cli_session_command(&config, raw_message).await? executes before
normalizing agent flags (e.g., applying plan to config.agent.execution_mode) and
validating unsupported overrides like --peripheral, causing incorrect
CommandContext construction for inputs like `--plan --message "/resume"` or
silently ignoring invalid --peripheral flags; move or duplicate the
flag-normalization and validation logic (the code that sets
config.agent.execution_mode from plan and rejects unsupported --peripheral
overrides) to run before invoking maybe_handle_cli_session_command so the CLI
contract and error reporting are preserved for the slash-command fast path and
CommandContext is built from the normalized config.

In
`@openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md`:
- Around line 166-170: The design doc's CommandCaller enum is missing the
DerivedCliScope variant that exists in the implementation; update the design.md
CommandCaller declaration to include the fourth variant DerivedCliScope (with
the same field shape used in the codebase, e.g., any channel/scope_key or
cli-specific fields used by the implementation) so the design matches the enum
defined in types.rs (ensure the names VerifiedTokenHash, DerivedChannelScope,
DerivedCliScope, and Unavailable are present and described).

In
`@openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md`:
- Line 40: The archived proposal references the live spec path
`openspec/changes/slash-command-context-permissions-result-contract/specs/`,
which is now stale; update the reference in the document to the archived specs
path (e.g.,
`openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/`)
or remove the live-directory reference if archives do not include a specs/
subtree so the archived proposal points to an accurate location; edit the single
occurrence of the path string in the proposal.md to reflect the correct archived
path or to indicate the specs are in the live change directory only.

In
`@openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md`:
- Line 13: Add a dedicated scenario that tests the rule "If no verifiable or
derivable caller scope can be established for an authorization-sensitive
`/resume` operation, the runtime MUST return an explicit authorization-denied or
unsupported outcome instead of broadening visibility"; construct the scenario to
send a `/resume` request with missing/invalid/unverifiable caller scope (e.g.,
absent claims, tampered token, or unable to derive scope from context), assert
the runtime returns an explicit denial (authorization-denied/unsupported error
or 403-like outcome) rather than returning broader visibility or success, and
place this scenario alongside the existing sessions `/resume` scenarios so it
covers the previously untested path referenced by the rule.

In
`@openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md`:
- Line 17: Update the sentence in verify-report.md that reads "All tasks in
`openspec/changes/slash-command-context-permissions-result-contract/tasks.md`
remain marked complete." so it points to the archived tasks list instead of the
pre-archive location; replace the referenced tasks.md path with the
corresponding archived path under changes/archive/.../tasks.md so the link stays
valid after the active change directory is cleaned up.

---

Outside diff comments:
In `@clients/agent-runtime/src/gateway/mod.rs`:
- Around line 1975-1998: Move the idempotency reservation before the
preview-mode short-circuit so preview slash-command requests still go through
duplicate suppression: call webhook_idempotency_key(&headers) and
record_if_new(...) on state.idempotency_store before checking is_preview &&
!dispatcher_enabled; on duplicate return
webhook_duplicate_response(idempotency_key) as before; if you reserved a key and
then still short-circuit via canonical_outcome_early_response(...).await, ensure
you call release_idempotency_key(&state, None, persist_idempotency) or
release_idempotency_key(&state, None, reserved_idempotency_key) as appropriate
to avoid leaking reservations; preserve the existing persist_idempotency
handling from canonical_outcome_early_response.

In `@clients/agent-runtime/src/session_commands/service.rs`:
- Around line 576-589: The truncation realignment in the function that enforces
MAX_CONTEXT_LENGTH uses context.find("\n- ") and then creates adjustment via
context.chars().skip(pos) which yields a string starting with "\n- ", so the
conditional adjustment.starts_with("- ") never matches; update the logic in that
block (where context is reassigned after computing excess and pos) to either
skip one more char (pos + 1) or check for adjustment.starts_with("\n- ") and
then strip the leading newline before assigning to context so list entries
beginning with "- " are correctly realigned.
🪄 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: 5fb42758-5879-486f-bf1b-5fc239dccdf3

📥 Commits

Reviewing files that changed from the base of the PR and between 72d1eff and 8dde3b4.

📒 Files selected for processing (21)
  • clients/agent-runtime/crates/corvus-traits/src/memory.rs
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/memory/sqlite.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • clients/agent-runtime/src/session_commands/mod.rs
  • clients/agent-runtime/src/session_commands/registry.rs
  • clients/agent-runtime/src/session_commands/service.rs
  • clients/agent-runtime/src/session_commands/types.rs
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/exploration.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/slash-command-registry/spec.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/state.yaml
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/tasks.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md
  • openspec/specs/sessions/spec.md
  • openspec/specs/slash-command-registry/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). (1)
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (9)
**/*

⚙️ 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-15-slash-command-context-permissions-result-contract/state.yaml
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/exploration.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/tasks.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md
  • clients/agent-runtime/src/main.rs
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md
  • clients/agent-runtime/crates/corvus-traits/src/memory.rs
  • openspec/specs/slash-command-registry/spec.md
  • clients/agent-runtime/src/memory/sqlite.rs
  • clients/agent-runtime/src/session_commands/mod.rs
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/slash-command-registry/spec.md
  • openspec/specs/sessions/spec.md
  • clients/agent-runtime/src/session_commands/registry.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/session_commands/service.rs
  • clients/agent-runtime/src/session_commands/types.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-15-slash-command-context-permissions-result-contract/proposal.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/exploration.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/tasks.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md
  • openspec/specs/slash-command-registry/spec.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/slash-command-registry/spec.md
  • openspec/specs/sessions/spec.md
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/main.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/memory/sqlite.rs
  • clients/agent-runtime/src/session_commands/mod.rs
  • clients/agent-runtime/src/session_commands/registry.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/session_commands/service.rs
  • clients/agent-runtime/src/session_commands/types.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/main.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/crates/corvus-traits/src/memory.rs
  • clients/agent-runtime/src/memory/sqlite.rs
  • clients/agent-runtime/src/session_commands/mod.rs
  • clients/agent-runtime/src/session_commands/registry.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/session_commands/service.rs
  • clients/agent-runtime/src/session_commands/types.rs
clients/agent-runtime/src/main.rs

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

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
Keep startup path lean and avoid heavy initialization in command parsing flow

Files:

  • clients/agent-runtime/src/main.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/main.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/crates/corvus-traits/src/memory.rs
  • clients/agent-runtime/src/memory/sqlite.rs
  • clients/agent-runtime/src/session_commands/mod.rs
  • clients/agent-runtime/src/session_commands/registry.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/session_commands/service.rs
  • clients/agent-runtime/src/session_commands/types.rs
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/webhook_dispatch.rs
  • clients/agent-runtime/src/gateway/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/webhook_dispatch.rs
  • clients/agent-runtime/src/gateway/mod.rs
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 (10)
📓 Common learnings
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
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
📚 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:

  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/tasks.md
  • clients/agent-runtime/src/main.rs
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md
  • clients/agent-runtime/src/session_commands/mod.rs
  • clients/agent-runtime/src/session_commands/registry.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/session_commands/service.rs
  • clients/agent-runtime/src/session_commands/types.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/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Applied to files:

  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/session_commands/mod.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • 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:

  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/tasks.md
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • clients/agent-runtime/src/channels/mod.rs
  • 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/main.rs : Keep startup path lean and avoid heavy initialization in command parsing flow

Applied to files:

  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/session_commands/mod.rs
  • clients/agent-runtime/src/session_commands/registry.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/session_commands/service.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:

  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/pre_execution/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/**/*.rs : Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Applied to files:

  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/memory/sqlite.rs
  • clients/agent-runtime/src/pre_execution/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/tools/**/*.rs : Implement `Tool` trait in `src/tools/` with strict parameter schema, validate and sanitize all inputs, and return structured `ToolResult` without panics in runtime path

Applied to files:

  • clients/agent-runtime/src/main.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/{security,gateway,tools}/**/*.rs : Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Applied to files:

  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/pre_execution/mod.rs
  • 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/**/Cargo.toml : Do not add heavy dependencies for minor convenience; justify new crate additions

Applied to files:

  • clients/agent-runtime/src/main.rs
🪛 LanguageTool
openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md

[grammar] ~7-~7: Ensure spelling is correct
Context: ...vice shape, but replaces the narrow and stringly contracts with typed runtime models: 1...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.22.0)
openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md

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

(MD022, blanks-around-headings)


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

(MD022, blanks-around-headings)

openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/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] 49-49: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


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

(MD022, blanks-around-headings)

🔇 Additional comments (17)
openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md (1)

11-12: Strong auth-scope boundary definition here.

The distinction between authenticated gateway identity and derived channel/CLI identity is clear and actionable, and it aligns well with caller-scoped /resume enforcement.

Also applies to: 34-40

openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md (1)

1-129: Design document is well-structured and aligns with implementation.

Architecture decisions, data flow diagrams, and interface contracts are clear. The testing strategy covers the key layers. Line 7's "stringly" is valid jargon (not a typo).

clients/agent-runtime/src/session_commands/registry.rs (3)

131-150: LGTM: Dispatch correctly propagates typed context and outcomes.

The dispatch method cleanly forwards CommandContext through to handlers and preserves SessionCommandOutcome without flattening. The session_id clone at line 47 (in pre_execution) is necessary since context ownership transfers to the handler.


175-243: LGTM: Built-in registrations use typed requirement metadata.

Clean separation of capability/permission/backend constants. The /resume command correctly declares RequiresCallerScope and RequiresResumableSessionVisibility permissions alongside the SessionLifecycle capability.


320-362: LGTM: Invocation validation returns typed failures.

validate_invocation correctly returns SessionCommandFailure with InvalidArguments kind instead of a separate error type, keeping the outcome model consistent.

openspec/specs/sessions/spec.md (1)

276-318: LGTM: /resume spec correctly requires typed context authorization.

The updated requirements clearly specify:

  • Caller-scoped visibility for both listing and targeted resume
  • Distinct handling of authenticated vs derived vs unavailable caller identity
  • Explicit denial when caller scope cannot be established

This aligns with the service.rs implementation using context.caller.scope_key() and get_resumable_session_for_scope.

openspec/specs/slash-command-registry/spec.md (2)

42-89: LGTM: Typed execution context contract is well-specified.

The spec correctly requires preservation of:

  • Session identity
  • Typed caller identity (authenticated/derived/unavailable distinction per CommandCaller enum)
  • Originating ingress kind
  • Execution mode
  • Evaluated requirement facts

Scenarios cover gateway authenticated vs CLI/channel derived identity semantics.


91-111: LGTM: Non-lossy outcome contract prevents error flattening.

The spec explicitly requires machine-readable SessionCommandFailureKind variants (authorization-denied, unsupported-backend, etc.) to remain distinguishable, matching the implementation in types.rs:374-385.

clients/agent-runtime/src/pre_execution/mod.rs (2)

41-57: LGTM: Ingress evaluation correctly propagates typed context and outcomes.

The session_id clone on line 47 is necessary since context ownership transfers to dispatch. The typed SessionCommandOutcome is preserved without flattening into success/failure booleans.


311-363: LGTM: Context builder tests verify transport-specific identity semantics.

Good coverage ensuring:

  • CLI uses DerivedCliScope caller variant
  • Gateway HTTP uses VerifiedTokenHash for authenticated callers
  • Channel uses DerivedChannelScope with channel name preserved
openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/slash-command-registry/spec.md (1)

1-126: LGTM: Delta spec cleanly documents contract additions and modifications.

The change archive document clearly separates ADDED requirements (typed context, typed metadata, non-lossy outcomes) from MODIFIED requirements (descriptor contract, transport parity), with accurate parenthetical notes explaining previous behavior.

clients/agent-runtime/src/session_commands/service.rs (2)

192-338: LGTM: /resume enforces caller-scoped authorization correctly.

The implementation:

  1. Denies with MissingCallerScope when context.caller.scope_key() is None (line 201-208)
  2. Uses get_resumable_session_for_scope to validate target visibility against caller scope (line 211-225)
  3. Returns PermissionDenied when the scoped lookup fails (line 221-224)
  4. Lists only caller-visible sessions via list_resumable_sessions(Some(caller_scope_key), ...) (line 307-313)

This correctly implements the spec requirement that /resume visibility follows caller-scoped boundaries.


468-494: LGTM: Storage errors are sanitized before user-facing exposure.

map_storage_error correctly:

  1. Detects unsupported backend errors and maps to UnsupportedBackend
  2. Sanitizes other errors via sanitize_storage_error (which redacts paths/credentials)
  3. Logs full error detail internally before returning sanitized message

As per coding guidelines: sensitive information is not logged in user-facing messages.

clients/agent-runtime/src/session_commands/types.rs (4)

9-46: LGTM: Storage error sanitization is thorough.

The sanitize_storage_error function:

  1. Redacts file paths and connection strings via regex
  2. Maps common error types to fixed user-friendly messages
  3. Falls back to generic "storage unavailable" rather than leaking sanitized text
  4. Logs full detail internally at debug level

As per coding guidelines: secrets and sensitive payloads are never exposed in user-facing output.


102-235: LGTM: CommandContext builders correctly preserve transport identity semantics.

Each builder (for_cli, for_gateway_http, for_gateway_stream, for_webhook, for_channel) maps to the appropriate CommandCaller variant:

  • Gateway paths use VerifiedTokenHash for authenticated callers
  • CLI uses DerivedCliScope
  • Channel uses DerivedChannelScope with channel name

The CommandContextFacts are derived once at construction time, avoiding repeated interpretation.


271-288: LGTM: CommandCaller::scope_key() correctly distinguishes caller identity.

The method returns Some(&str) for all identity variants except Unavailable, enabling authorization checks to uniformly query scope availability without pattern matching at every call site.


332-385: LGTM: Typed outcome model preserves machine-readable failure kinds.

SessionCommandOutcome with Success/Failure variants and SessionCommandFailureKind enum keeps authorization denials (PermissionDenied, MissingCallerScope) distinct from other failures, satisfying the non-lossy internal contract requirement.

Comment thread clients/agent-runtime/src/channels/mod.rs
Comment thread clients/agent-runtime/src/gateway/mod.rs
Comment on lines +6168 to +6177
// `/tldr` without persisted transcript returns a deterministic SSE success payload
assert_eq!(resp.status(), StatusCode::OK);
let collected = resp.into_body().collect().await.unwrap();
let body_str = std::str::from_utf8(&collected.to_bytes())
.unwrap()
.to_owned();

// Debug: print body if test fails
if !body_str.contains("event: chunk") {
eprintln!("body_str: {:?}", body_str);
}

// Streaming stays on the shared ingress seam and returns the slash-session response without
// invoking the provider path.
assert!(body_str.contains("event: chunk"));
assert!(body_str.contains("No persisted session transcript is available yet."));
assert!(body_str.contains("event: done"));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add the missing failure-path stream regression test.

This only locks in the success envelope. The new stream contract also sets HTTP 422 and emits event: error for SessionCommandOutcome::Failure; that behavior should have a direct test as well.

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

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

In `@clients/agent-runtime/src/gateway/mod.rs` around lines 6168 - 6177, Add a
regression test for the failure-path of the new stream contract: when the
session command outcome is SessionCommandOutcome::Failure, assert that the
response status is StatusCode::UNPROCESSABLE_ENTITY (422) and that the collected
response body (the body_str built from resp.into_body().collect()) contains the
SSE error envelope ("event: error") and the expected failure message instead of
the success chunks/done lines; update or add a test near the existing
success-case assertions that sets up a Failure outcome and verifies
resp.status() == StatusCode::UNPROCESSABLE_ENTITY and body_str.contains("event:
error") plus the failure text.

Comment thread clients/agent-runtime/src/gateway/webhook_dispatch.rs
Comment thread clients/agent-runtime/src/main.rs
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: 4

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/gateway/webhook_dispatch.rs (1)

459-462: 🧹 Nitpick | 🔵 Trivial

Redundant recomputation of clamped execution mode.

resolve_webhook_execution_mode is called again at line 461-462 even though clamped_mode was already computed at lines 397-398. This is wasteful and could diverge if logic changes in one place but not the other.

Proposed fix
     let mut effective_config = config.clone();
-    // Clamp request execution mode against server-configured mode (fail-closed to more restrictive)
-    effective_config.agent.execution_mode =
-        resolve_webhook_execution_mode(config.agent.execution_mode, Some(request.execution_mode));
+    // Use the already-clamped mode computed for ingress evaluation
+    effective_config.agent.execution_mode = clamped_mode;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/gateway/webhook_dispatch.rs` around lines 459 -
462, The code recomputes the clamped execution mode by calling
resolve_webhook_execution_mode again when building effective_config; instead
reuse the previously computed clamped_mode (the variable calculated earlier)
instead of calling resolve_webhook_execution_mode a second time. Update the
assignment to effective_config.agent.execution_mode to use clamped_mode (or its
equivalent) so the value is not recomputed and the two places remain consistent
(refer to resolve_webhook_execution_mode, effective_config, config.clone(), and
clamped_mode).
♻️ Duplicate comments (1)
clients/agent-runtime/src/gateway/mod.rs (1)

6200-6209: ⚠️ Potential issue | 🟡 Minor

Add the failure-path stream regression too.

This only locks in the success envelope. The new SessionCommandOutcome::Failure branch now returns HTTP 422 and emits event: error; without a companion test, that contract can still regress silently. Add a case that leaves the slash command unresolved and assert resp.status() == StatusCode::UNPROCESSABLE_ENTITY, body_str.contains("event: error"), and the failure message.

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

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

In `@clients/agent-runtime/src/gateway/mod.rs` around lines 6200 - 6209, Add a
regression test exercising the failure-path for SessionCommandOutcome::Failure:
create a case similar to the existing success-path but leave the slash command
unresolved so the gateway returns HTTP 422 and an SSE error envelope; then
assert resp.status() == StatusCode::UNPROCESSABLE_ENTITY, collect the body into
body_str (like the success test) and assert body_str.contains("event: error")
and that it contains the expected failure message (e.g., "No persisted session
transcript is available yet." or the specific failure text your code emits).
Place this alongside the existing assertions that check the success SSE (reusing
resp, collected, body_str variables) so the contract for failure emits is
covered.
🤖 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/gateway/mod.rs`:
- Around line 1764-1775: The ingress contexts are currently built using the
server default execution mode, which causes requests that carried an explicit
execution_mode (e.g., "plan") to be downgraded before evaluate_ingress() and can
make permission/requirement checks incorrect; to fix, change
canonical_outcome_early_response(...) to accept the already-resolved effective
execution mode as an argument and pass that mode into
CommandContext::for_gateway_http (and similarly thread the mode into
for_gateway_stream(...)), then ensure the same effective mode is used later when
constructing the dispatcher/execution context so evaluate_ingress() and
subsequent checks run against the correct mode rather than the server default.

In `@clients/agent-runtime/src/session_commands/service.rs`:
- Around line 576-585: The truncation branch for context currently discards the
truncated string when no "\n- " boundary is found; update the logic around
context, excess, and the find("\n- ") check so you first compute the truncated
candidate (e.g., let truncated =
context.chars().skip(excess).collect::<String>()), then attempt to locate "\n- "
inside that truncated candidate and only replace context with the adjusted slice
if the boundary is found and starts with "- "; otherwise retain the truncated
candidate as the fallback (ensuring it is non-empty and still within
MAX_CONTEXT_LENGTH). Apply this change in the block handling MAX_CONTEXT_LENGTH
(the variables/functions: context, excess, MAX_CONTEXT_LENGTH, adjustment,
find("\n- ")) so you never silently lose content when no list boundary exists.

In
`@openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md`:
- Line 1: The first line uses a level-2 heading ("## Verification Report") and
triggers MD041; change it to a top-level heading by replacing "## Verification
Report" with "# Verification Report" so the document starts with a single
top-level heading and satisfies the linter rule.
- Around line 49-53: Add a blank line after the "#### Quality gates" and "####
Targeted behavioral tests" heading lines so each heading is followed by an empty
line (fixing MD022); update the verify-report.md content around the "Quality
gates" and "Targeted behavioral tests" headings to include a single blank line
immediately after each heading.

---

Outside diff comments:
In `@clients/agent-runtime/src/gateway/webhook_dispatch.rs`:
- Around line 459-462: The code recomputes the clamped execution mode by calling
resolve_webhook_execution_mode again when building effective_config; instead
reuse the previously computed clamped_mode (the variable calculated earlier)
instead of calling resolve_webhook_execution_mode a second time. Update the
assignment to effective_config.agent.execution_mode to use clamped_mode (or its
equivalent) so the value is not recomputed and the two places remain consistent
(refer to resolve_webhook_execution_mode, effective_config, config.clone(), and
clamped_mode).

---

Duplicate comments:
In `@clients/agent-runtime/src/gateway/mod.rs`:
- Around line 6200-6209: Add a regression test exercising the failure-path for
SessionCommandOutcome::Failure: create a case similar to the existing
success-path but leave the slash command unresolved so the gateway returns HTTP
422 and an SSE error envelope; then assert resp.status() ==
StatusCode::UNPROCESSABLE_ENTITY, collect the body into body_str (like the
success test) and assert body_str.contains("event: error") and that it contains
the expected failure message (e.g., "No persisted session transcript is
available yet." or the specific failure text your code emits). Place this
alongside the existing assertions that check the success SSE (reusing resp,
collected, body_str variables) so the contract for failure emits is covered.
🪄 Autofix (Beta)

❌ Autofix failed (check again to retry)

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: 0c10ea50-a464-41a9-ba7e-8194f43ac308

📥 Commits

Reviewing files that changed from the base of the PR and between 8dde3b4 and 08f33f0.

📒 Files selected for processing (9)
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/session_commands/service.rs
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.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). (1)
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{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-15-slash-command-context-permissions-result-contract/proposal.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md
**/*

⚙️ 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-15-slash-command-context-permissions-result-contract/proposal.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md
  • clients/agent-runtime/src/channels/mod.rs
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/session_commands/service.rs
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
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/channels/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/session_commands/service.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/channels/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/session_commands/service.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/channels/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/session_commands/service.rs
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/webhook_dispatch.rs
  • clients/agent-runtime/src/gateway/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/webhook_dispatch.rs
  • clients/agent-runtime/src/gateway/mod.rs
clients/agent-runtime/src/main.rs

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

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
Keep startup path lean and avoid heavy initialization in command parsing flow

Files:

  • clients/agent-runtime/src/main.rs
🧠 Learnings (10)
📓 Common learnings
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
📚 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:

  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md
  • clients/agent-runtime/src/channels/mod.rs
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/session_commands/service.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/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Applied to files:

  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md
  • openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md
  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/main.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
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/main.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
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/main.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/{security,gateway,tools}/**/*.rs : Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Applied to files:

  • clients/agent-runtime/src/channels/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/main.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:

  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/main.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/**/*.rs : Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Applied to files:

  • clients/agent-runtime/src/main.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/tools/**/*.rs : Implement `Tool` trait in `src/tools/` with strict parameter schema, validate and sanitize all inputs, and return structured `ToolResult` without panics in runtime path

Applied to files:

  • clients/agent-runtime/src/main.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/**/Cargo.toml : Do not add heavy dependencies for minor convenience; justify new crate additions

Applied to files:

  • clients/agent-runtime/src/main.rs
🪛 LanguageTool
openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md

[grammar] ~7-~7: Ensure spelling is correct
Context: ...vice shape, but replaces the narrow and stringly contracts with typed runtime models: 1...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.22.0)
openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/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] 49-49: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


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

(MD022, blanks-around-headings)

🔇 Additional comments (17)
clients/agent-runtime/src/session_commands/service.rs (4)

192-338: LGTM on /resume caller-scope enforcement.

The authorization flow correctly:

  1. Requires a caller scope key (lines 201-208)
  2. Uses scoped lookup via get_resumable_session_for_scope (lines 211-225)
  3. Returns PermissionDenied when the target session isn't in the caller's scope
  4. Validates target state (suspended, not ended) before allowing resume

This tightens the memory boundary as intended by the PR objectives.


23-79: LGTM on /tldr typed outcome conversion.

The handler correctly:

  • Returns SessionCommandOutcome via outcome_from_result
  • Uses structured failure kinds (UnsupportedBackend, InvalidState, etc.)
  • Sanitizes storage errors through map_storage_error

468-493: Good: Storage errors are sanitized before being exposed in failure messages.

The map_storage_error helper correctly uses sanitize_storage_error to prevent leaking sensitive path or credential information in user-facing messages while logging the full detail internally at error level.


1135-1149: Good test coverage for storage error sanitization.

Test resume_list_storage_failures_are_sanitized properly validates that:

  • Sensitive paths like /tmp/secret.db are not exposed in the failure message
  • The sanitized message contains "storage access denied" instead
clients/agent-runtime/src/gateway/webhook_dispatch.rs (3)

396-412: Good: Execution mode is now clamped before ingress evaluation.

This correctly addresses the security concern where evaluate_ingress could observe a less restrictive mode than what would actually be enforced. The clamped mode is computed upfront and passed to CommandContext::for_webhook.


417-436: LGTM on SessionCommand outcome mapping.

The new typed contract is correctly handled:

  • SessionCommandOutcome::SuccessWebhookTerminalOutcome::Completed
  • SessionCommandOutcome::FailureWebhookTerminalOutcome::Failed
  • Response text is sourced from the appropriate message field

1068-1101: Good regression test for slash command interception.

Test execute_intercepts_slash_session_commands_before_provider_execution validates that:

  • Provider is not called when a slash command is detected
  • Outcome is Failed for unsupported backend (TestMemory returns "test", not "sqlite")
  • Response text contains the expected error message
clients/agent-runtime/src/channels/mod.rs (2)

704-712: Execution mode is now preserved through ingress evaluation.

process_channel_message now threads ctx.config.agent.execution_mode into CommandContext::for_channel, so channel slash commands see the same plan/standard mode as the rest of the turn. That closes the earlier authz drift from the hardcoded standard mode.

Also applies to: 1854-1888


3240-3248: Plan-mode regression coverage looks solid.

The added ExecutionMode::Plan test, alongside the explicit standard-mode call sites, gives this channel path good coverage against slipping back to standard-only behavior. Based on learnings: Implement Channel trait in src/channels/ with consistent send, listen, and health_check semantics and cover auth/allowlist/health behavior with tests

Also applies to: 3275-3283, 3290-3313

clients/agent-runtime/src/main.rs (4)

1401-1424: Previous issue addressed: flag normalization now occurs before slash-command fast path.

The refactor correctly moves execution_mode normalization and peripheral validation ahead of the maybe_handle_cli_session_command call. This ensures the CommandContext is built with the correct plan-mode state and unsupported overrides are rejected early.


1530-1565: Typed context construction and outcome handling looks correct.

The refactor properly:

  • Derives CommandSessionSource::Explicit vs Generated from CORVUS_SESSION_ID presence
  • Passes the normalized config.agent.execution_mode into CommandContext::for_cli
  • Maps SessionCommandOutcome::SuccessOk(Some(...)) and FailureErr(...) preserving explicit error semantics

1579-1585: LGTM!

Consistent with the agent command refactor. apply_code_session_config normalizes execution_mode before the slash command check.


1466-1485: LGTM!

Clean separation: execution-mode normalization happens early for the slash-command path, while provider/model/temperature overrides apply only when continuing to full agent execution.

openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/proposal.md (1)

1-65: Proposal document aligns with implementation.

The spec accurately describes the typed CommandContext, SessionCommandOutcome, and /resume caller-scope enforcement that the code implements. The path reference on line 40 now correctly points to the archived location.

openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md (1)

17-17: Tasks path reference is now correct.

Line 17 properly references the archived tasks.md path. The previously flagged issue has been resolved.

openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/specs/sessions/spec.md (1)

42-49: The previously requested scenario for unverifiable caller scope denial has been added.

This scenario now tests the authorization rule from line 13, covering absent/tampered/unverifiable caller scope cases and asserting explicit denial without broadening visibility.

openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/design.md (1)

166-171: The CommandCaller enum now includes the DerivedCliScope variant.

The design documentation has been updated to match the implementation, showing all four variants as defined in the codebase.

Comment on lines 1764 to +1775
async fn canonical_outcome_early_response(
state: &AppState,
session_id: &str,
session_source: crate::session_commands::CommandSessionSource,
scrubbed_message: &str,
caller_token_hash: Option<&str>,
) -> Option<(WebhookResponse, bool)> {
match crate::pre_execution::evaluate_ingress(
state.mem.as_ref(),
let context = crate::session_commands::CommandContext::for_gateway_http(
session_id,
scrubbed_message,
caller_token_hash,
)
.await
session_source,
state.config.lock().agent.execution_mode,
caller_token_hash.map(str::to_string),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Pass the effective execution mode into the typed command context.

Both ingress contexts are built from the server default mode, so a request with {"execution_mode":"plan"} on a Standard server is downgraded to Standard before evaluate_ingress(). That drops the plan-mode bit from the new typed contract and can make requirement/permission checks run against the wrong mode. Thread the already-resolved mode through canonical_outcome_early_response() and for_gateway_stream(...), then reuse that same value for dispatcher execution.

♻️ Proposed fix
 async fn canonical_outcome_early_response(
     state: &AppState,
     session_id: &str,
     session_source: crate::session_commands::CommandSessionSource,
+    execution_mode: ExecutionMode,
     scrubbed_message: &str,
     caller_token_hash: Option<&str>,
 ) -> Option<(WebhookResponse, bool)> {
     let context = crate::session_commands::CommandContext::for_gateway_http(
         session_id,
         session_source,
-        state.config.lock().agent.execution_mode,
+        execution_mode,
         caller_token_hash.map(str::to_string),
     );
     let config = state.config.lock().clone();
     let server_execution_mode = config.agent.execution_mode;
+    let resolved_execution_mode =
+        resolve_webhook_execution_mode(server_execution_mode, webhook_body.execution_mode);
     let dispatcher_enabled = webhook_dispatcher_enabled(&config);
         if let Some((response, persist_idempotency)) = canonical_outcome_early_response(
             &state,
             &session_id,
             http_source,
+            resolved_execution_mode,
             &scrubbed_message,
             token_hash.as_deref(),
         )
     let ingress_context = crate::session_commands::CommandContext::for_gateway_stream(
         &session_id,
         match session_source {
             webhook_dispatch::WebhookSessionSource::Explicit => {
                 crate::session_commands::CommandSessionSource::Explicit
             }
             webhook_dispatch::WebhookSessionSource::Generated => {
                 crate::session_commands::CommandSessionSource::Generated
             }
         },
-        server_execution_mode,
+        resolve_webhook_execution_mode(server_execution_mode, webhook_body.execution_mode),
         token_hash.clone(),
     );
As per coding guidelines, "`**/*`: Security first, performance second. Validate input boundaries, auth/authz implications, and secret management. Look for behavioral regressions, missing tests, and contract breaks across modules."

Also applies to: 2217-2229

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

In `@clients/agent-runtime/src/gateway/mod.rs` around lines 1764 - 1775, The
ingress contexts are currently built using the server default execution mode,
which causes requests that carried an explicit execution_mode (e.g., "plan") to
be downgraded before evaluate_ingress() and can make permission/requirement
checks incorrect; to fix, change canonical_outcome_early_response(...) to accept
the already-resolved effective execution mode as an argument and pass that mode
into CommandContext::for_gateway_http (and similarly thread the mode into
for_gateway_stream(...)), then ensure the same effective mode is used later when
constructing the dispatcher/execution context so evaluate_ingress() and
subsequent checks run against the correct mode rather than the server default.

Comment on lines 576 to 585
if context.chars().count() > MAX_CONTEXT_LENGTH {
let prefix: String = context.chars().take(MAX_CONTEXT_LENGTH).collect();
// Find last occurrence of "\n- " to keep only complete entries
if let Some(last_entry_pos) = prefix.rfind("\n- ") {
context = prefix[..last_entry_pos].to_string();
} else {
context = prefix;
let excess = context.chars().count() - MAX_CONTEXT_LENGTH;
context = context.chars().skip(excess).collect::<String>();
// Ensure we don't start mid-entry: skip past the newline to land on the "- " of the list entry
if let Some(pos) = context.find("\n- ") {
let adjustment: String = context.chars().skip(pos + 1).collect();
if adjustment.starts_with("- ") {
context = adjustment;
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Truncation logic may drop all content if no "\n- " is found.

When enforcing MAX_CONTEXT_LENGTH, the code skips excess characters from the start, then looks for "\n- " to land on a list boundary. If no such pattern exists in the truncated string (e.g., context is mostly summary with no list entries), the original truncated content is silently discarded and context remains unchanged—potentially still exceeding the limit or becoming empty.

Consider adding a fallback that keeps the truncated string when no list boundary is found:

Proposed fix
     if context.chars().count() > MAX_CONTEXT_LENGTH {
         let excess = context.chars().count() - MAX_CONTEXT_LENGTH;
-        context = context.chars().skip(excess).collect::<String>();
-        // Ensure we don't start mid-entry: skip past the newline to land on the "- " of the list entry
+        let truncated: String = context.chars().skip(excess).collect();
+        // Ensure we don't start mid-entry: skip past the newline to land on the "- " of the list entry
-        if let Some(pos) = context.find("\n- ") {
-            let adjustment: String = context.chars().skip(pos + 1).collect();
+        context = if let Some(pos) = truncated.find("\n- ") {
+            let adjustment: String = truncated.chars().skip(pos + 1).collect();
             if adjustment.starts_with("- ") {
-                context = adjustment;
+                adjustment
+            } else {
+                truncated
             }
-        }
+        } else {
+            truncated
+        };
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/agent-runtime/src/session_commands/service.rs` around lines 576 -
585, The truncation branch for context currently discards the truncated string
when no "\n- " boundary is found; update the logic around context, excess, and
the find("\n- ") check so you first compute the truncated candidate (e.g., let
truncated = context.chars().skip(excess).collect::<String>()), then attempt to
locate "\n- " inside that truncated candidate and only replace context with the
adjusted slice if the boundary is found and starts with "- "; otherwise retain
the truncated candidate as the fallback (ensuring it is non-empty and still
within MAX_CONTEXT_LENGTH). Apply this change in the block handling
MAX_CONTEXT_LENGTH (the variables/functions: context, excess,
MAX_CONTEXT_LENGTH, adjustment, find("\n- ")) so you never silently lose content
when no list boundary exists.

@@ -0,0 +1,91 @@
## Verification Report
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Minor: First line should be a top-level heading.

Static analysis flagged MD041. Consider changing line 1 from ## Verification Report to # Verification Report for proper document structure.

Proposed fix
-## Verification Report
+# Verification Report
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## Verification Report
# Verification Report
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

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

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

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

In
`@openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md`
at line 1, The first line uses a level-2 heading ("## Verification Report") and
triggers MD041; change it to a top-level heading by replacing "## Verification
Report" with "# Verification Report" so the document starts with a single
top-level heading and satisfies the linter rule.

Comment on lines +49 to +53
#### Quality gates
- `cargo fmt --all -- --check` ✅ passed
- `cargo clippy --manifest-path Cargo.toml --all-targets -- -D warnings` ✅ passed

#### Targeted behavioral tests
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Minor: Add blank lines after headings for markdown compliance.

MD022 flagged missing blank lines below headings at lines 49 and 53.

Proposed fix
 #### Quality gates
+
 - `cargo fmt --all -- --check` ✅ passed
 #### Targeted behavioral tests
+
 - `cargo test --manifest-path Cargo.toml legacy_webhook_preview_intercepts_slash_session_commands` ✅
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#### Quality gates
- `cargo fmt --all -- --check` ✅ passed
- `cargo clippy --manifest-path Cargo.toml --all-targets -- -D warnings` ✅ passed
#### Targeted behavioral tests
#### Quality gates
- `cargo fmt --all -- --check` ✅ passed
- `cargo clippy --manifest-path Cargo.toml --all-targets -- -D warnings` ✅ passed
#### Targeted behavioral tests
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

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

(MD022, blanks-around-headings)


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

(MD022, blanks-around-headings)

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

In
`@openspec/changes/archive/2026-04-15-slash-command-context-permissions-result-contract/verify-report.md`
around lines 49 - 53, Add a blank line after the "#### Quality gates" and "####
Targeted behavioral tests" heading lines so each heading is followed by an empty
line (fixing MD022); update the verify-report.md content around the "Quality
gates" and "Targeted behavioral tests" headings to include a single blank line
immediately after each heading.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

An unexpected error occurred while generating fixes: Not Found - https://docs.github.com/rest/git/refs#get-a-reference

@yacosta738 yacosta738 merged commit 63e1acd into develop Apr 16, 2026
7 checks passed
@yacosta738 yacosta738 deleted the feature/dallay-540-slash-command-context branch April 16, 2026 06:10
This was referenced Apr 29, 2026
@dallay-bot dallay-bot Bot mentioned this pull request May 3, 2026
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.

1 participant