Skip to content

fix(reborn): persist Slack host conversation bindings#5252

Open
serrrfirat wants to merge 5 commits into
mainfrom
codex/slack-durable-conversation-bindings
Open

fix(reborn): persist Slack host conversation bindings#5252
serrrfirat wants to merge 5 commits into
mainfrom
codex/slack-durable-conversation-bindings

Conversation

@serrrfirat

@serrrfirat serrrfirat commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • route Slack host-beta conversation binding and actor-pairing ports through the runtime-owned durable conversation service
  • fail Slack route construction loudly when durable conversation services cannot initialize
  • add a durable-backend restart regression that sends a Slack event, reopens the runtime from the same host root, and verifies the follow-up uses the existing thread
  • keep WebUI v2 chat submit disabled while an active run is still running, which unblocks the composer smoke check exposed on this PR

Change Type

  • Bug fix
  • New feature
  • Refactor
  • Documentation
  • CI/Infrastructure
  • Security
  • Dependencies

Linked Issue

None. This fixes the Slack host-beta restart binding loss reported from runtime logs.

Validation

  • cargo fmt --all -- --check
  • cargo clippy --all --benches --tests --examples --all-features -- -D warnings
  • cargo build
  • Relevant tests pass:
    • CARGO_INCREMENTAL=0 CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 cargo test -p ironclaw_reborn_composition --features test-support,webui-v2-beta,slack-v2-host-beta,libsql slack_host_beta::tests:: -- --nocapture
    • CARGO_INCREMENTAL=0 CARGO_PROFILE_DEV_DEBUG=0 CARGO_PROFILE_TEST_DEBUG=0 cargo clippy -p ironclaw_reborn_composition --features test-support,webui-v2-beta,slack-v2-host-beta,libsql --tests -- -D warnings
    • cargo check -p ironclaw_reborn_composition --features test-support,webui-v2-beta,slack-v2-host-beta,postgres
    • cargo check -p ironclaw_reborn_composition --features test-support,webui-v2-beta,slack-v2-host-beta
    • node --test crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/chat.test.mjs
    • cargo test -p ironclaw_webui_v2_static
    • git diff --check
    • /Users/firatsertgoz/.local/bin/python3.11 scripts/check_no_panics.py --base origin/main --head HEAD
  • cargo test --features integration if database-backed or integration behavior changed
  • Manual testing: local Playwright E2E could not be run because the active Python environment lacks playwright; CI runs the WebUI smoke test.
  • If a coding agent was used and supports it, review-pr or pr-shepherd --fix was run before requesting review: CodeRabbit feedback was fetched and addressed manually; CI is green on the pushed head.

Security Impact

This touches host-beta Slack event routing and conversation binding persistence. It does not weaken webhook verification, bearer auth, CORS/origin checks, outbound HTTP policy, or secret handling. The change replaces process-local conversation binding storage with existing host filesystem-backed conversation services for DB-backed profiles.

Reborn Trust-Boundary Checklist

  • Public policy/evidence/trust-bearing types: who can construct them? No new trusted inbound constructors or policy/evidence types are introduced.
  • Untrusted content enters prompts only through an envelope/escaping primitive. Slack event ingestion path is unchanged; only binding service wiring changes.
  • Hashes declare purpose; trust/binding/authenticity uses SHA-256/BLAKE3 or separate authenticity check. No hashing/authenticity behavior changes.
  • New/changed status, exit, policy, runtime, or error variants: downstream match sites audited. Command/output: rg "ConversationServicesUnavailable|SlackHostBetaBuildError" crates/ironclaw_reborn_composition/src.
  • Security/durability serde(default) fields fail closed or have migration tests. No new serde fields are added.
  • Queues/maps/buffers/counters have bounds and overflow-safe arithmetic. No new queue/map/buffer growth path is added.
  • Driver/operator-visible errors have stable class semantics (Transient, Permanent, Misconfigured, PolicyDenied or equivalent). Slack route construction now fails loudly with a conversation-services-unavailable build error if durable services cannot initialize.
  • Sandbox/native/host names accurately describe trust boundary. The new SlackConversationPorts type describes runtime-provided conversation service ports.

Database Impact

No database migration is needed. This uses the existing conversation filesystem abstraction and its configured backend; it does not add a Slack-specific table or schema. The durable restart regression is gated to libsql or postgres builds because no-DB builds intentionally use in-memory services.

Blast Radius

Touches Slack host-beta runtime composition, Slack route mount construction, dynamic Slack installation resolver wiring, and WebUI v2 chat submit gating. Primary risk is Slack route construction failing if durable conversation services are unavailable in a DB-backed hosted profile; that failure is intentional and operator-visible instead of silently losing restart state.

Rollback Plan

Revert this PR to restore the previous Slack host-beta in-memory conversation binding behavior. Rollback risk is recurrence of the original restart bug: Slack conversation-to-thread bindings are lost after process restart and follow-up delivery can fail or create the wrong conversation linkage.

Review Follow-Through

CodeRabbit left one actionable inline comment asking that the restart regression be durable-backend gated; this has been addressed with a #[cfg(any(feature = "libsql", feature = "postgres"))] gate on that test. Reviewer judgment is still needed on whether the Slack host-beta mount API async conversion is acceptable for callers.


Review track: C (security/runtime/DB/CI)

@railway-app railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-5252 June 25, 2026 16:45 Destroyed
@github-actions github-actions Bot added the size: L 200-499 changed lines label Jun 25, 2026
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Improved Slack host-beta routing to support multiple conversation service backends via unified conversation ports.
  • Bug Fixes
    • Chat composer now blocks message sending while a relevant run is active (e.g., queued/running), while keeping the composer usable.
  • Tests
    • Added unit coverage to verify sending is prevented during an active run without triggering the send action.

Walkthrough

Durable conversation initialization is split out, Slack host beta now threads conversation ports through mount and resolver construction, chat send disabling changes for active runs, and the affected mount builders and tests become async.

Changes

Slack conversation port threading

Layer / File(s) Summary
Durable conversation service helper
crates/ironclaw_reborn_composition/src/factory.rs
durable_trigger_conversation_services now forwards to a new durable_conversation_services helper that contains the filesystem-backed initialization and cache setup.
Conversation ports and async entrypoints
crates/ironclaw_reborn_composition/src/slack_host_beta.rs
SlackConversationPorts is added, the conversation-services-unavailable error variant is added, and the Slack host beta mount entrypoints now resolve conversation ports asynchronously.
Runtime resolver threading
crates/ironclaw_reborn_composition/src/slack_host_beta/runtime_setup.rs
build_runtime_mounts fetches conversation ports, DynamicSlackInstallationResolver stores them, and resolver construction forwards them into installation-record creation.
Chat send gating
crates/ironclaw_webui_v2_static/static/js/pages/chat/chat.js, crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/chat.test.mjs
Chat now derives send disabling from active run state in the current thread, and the new test covers the running-run case while keeping the composer enabled.
Async test call sites
crates/ironclaw_reborn_composition/src/slack_host_beta.rs
Tests now await the async mount builders across event-route, host-beta, admin/WebUI, provisioning, route-reachability, config, and hook-wiring cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • nearai/ironclaw#5235: Shares the Chat send-disabled / composer-enabled state change and test coverage.

Suggested reviewers

  • henrypark133
  • think-in-universe

Poem

A port was plucked from async air,
Then threaded through with careful care.
Mounts woke up and tests complied,
While chat stayed open, send denied.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title uses Conventional Commits format and clearly matches the Slack host conversation-binding fix.
Description check ✅ Passed The description matches the template well and fills the required sections with relevant details and validation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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


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

@github-actions github-actions Bot added risk: low Changes to docs, tests, or low-risk modules contributor: core 20+ merged PRs labels Jun 25, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the Slack host-beta implementation to support durable conversation services when the libsql or postgres features are enabled, rather than always relying on in-memory bindings. This is accomplished by introducing a SlackConversationPorts struct, making route mount building functions asynchronous, and passing the resolved conversation ports to the installation resolver. Additionally, tests have been updated and a new test has been added to verify that conversation bindings persist across runtime restarts. There are no review comments to address.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@crates/ironclaw_reborn_composition/src/slack_host_beta.rs`:
- Around line 1516-1582: This restart-regression test currently assumes
conversation bindings survive a runtime reopen even when the build uses
InMemoryConversationServices, which is only true for durable backends. Update
build_slack_events_route_mount_reuses_conversation_binding_after_runtime_reopen
to run only when libsql or postgres is enabled, or otherwise skip/assert the
in-memory fallback behavior, using the
runtime_with_root_and_host_egress_override and build_slack_events_route_mount
setup as the gate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 80eed385-d333-46f6-a3da-1cf882d9fa3c

📥 Commits

Reviewing files that changed from the base of the PR and between 6af6b93 and e5d17de.

📒 Files selected for processing (3)
  • crates/ironclaw_reborn_composition/src/factory.rs
  • crates/ironclaw_reborn_composition/src/slack_host_beta.rs
  • crates/ironclaw_reborn_composition/src/slack_host_beta/runtime_setup.rs

Comment thread crates/ironclaw_reborn_composition/src/slack_host_beta.rs
@railway-app railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-5252 June 25, 2026 16:56 Destroyed
@railway-app railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-5252 June 25, 2026 17:02 Destroyed
@github-actions github-actions Bot added size: XL 500+ changed lines and removed size: L 200-499 changed lines labels Jun 25, 2026
@railway-app

railway-app Bot commented Jun 25, 2026

Copy link
Copy Markdown

🚅 Deployed to the ironclaw-pr-5252 environment in ironclaw-ci-preview

Service Status Web Updated (UTC)
ironclaw ✅ Success (View Logs) Web Jun 25, 2026 at 9:11 pm

@railway-app railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-5252 June 25, 2026 17:52 Destroyed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
crates/ironclaw_webui_v2_static/static/js/pages/chat/chat.js (1)

92-105: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep the thread row in RUNNING while this new active-run gate is blocking sends.

This adds a current-thread busy state (activeRun.status running/queued with isProcessing === false), but the effect at Lines 201-216 still clears sidebar state whenever isProcessing is false. The new chat.test.mjs case covers exactly that rehydrated state, so the composer stays blocked while the thread row flips back to idle after 1.5s.

Suggested fix
   const activeRunBlocksSubmit = Boolean(
     activeThreadId &&
       activeRun?.runId &&
       activeRun.threadId === activeThreadId &&
       !pendingGate &&
       (!activeRun.status ||
         activeRun.status === "queued" ||
         activeRun.status === "running")
   );
@@
-    if (isProcessing) {
+    if (isProcessing || activeRunBlocksSubmit) {
       setThreadState(activeThreadId, THREAD_STATE.RUNNING);
       return undefined;
     }
@@
-  }, [activeThreadId, pendingGate, isProcessing]);
+  }, [activeThreadId, pendingGate, isProcessing, activeRunBlocksSubmit]);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/ironclaw_webui_v2_static/static/js/pages/chat/chat.js` around lines 92
- 105, The current-thread busy state is being cleared too early in the sidebar
sync logic, causing the thread row to flip back to idle while
`activeRunBlocksSubmit` is still blocking sends. Update the state reset/effect
in `chat.js` around the `isProcessing` handling so it preserves `RUNNING`/busy
status for the active thread whenever `activeRun.status` is `queued` or
`running` and `activeRun.threadId` matches `activeThreadId`, instead of clearing
sidebar state solely because `isProcessing` is false. Keep the composer gating
in sync with `activeRunBlocksSubmit` and the existing `composerSendDisabled`
logic so the rehydrated active-run case remains blocked.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@crates/ironclaw_webui_v2_static/static/js/pages/chat/chat.js`:
- Around line 92-105: The current-thread busy state is being cleared too early
in the sidebar sync logic, causing the thread row to flip back to idle while
`activeRunBlocksSubmit` is still blocking sends. Update the state reset/effect
in `chat.js` around the `isProcessing` handling so it preserves `RUNNING`/busy
status for the active thread whenever `activeRun.status` is `queued` or
`running` and `activeRun.threadId` matches `activeThreadId`, instead of clearing
sidebar state solely because `isProcessing` is false. Keep the composer gating
in sync with `activeRunBlocksSubmit` and the existing `composerSendDisabled`
logic so the rehydrated active-run case remains blocked.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8a24ddd6-3415-41b0-8d5e-7bb95c4d8f55

📥 Commits

Reviewing files that changed from the base of the PR and between 517715c and c06c483.

⛔ Files ignored due to path filters (1)
  • crates/ironclaw_webui_v2_static/static/dist/app.js is excluded by !**/dist/**
📒 Files selected for processing (2)
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/chat.js
  • crates/ironclaw_webui_v2_static/static/js/pages/chat/lib/chat.test.mjs

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

Labels

contributor: core 20+ merged PRs risk: low Changes to docs, tests, or low-risk modules size: XL 500+ changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant