Skip to content
This repository was archived by the owner on May 26, 2026. It is now read-only.

feat(kora): KR-SLACK-DM-PANEL — conversation view stub#120

Merged
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-SLACK-DM-PANEL
May 22, 2026
Merged

feat(kora): KR-SLACK-DM-PANEL — conversation view stub#120
rafe-walker merged 1 commit into
feature/phase2-upgradesfrom
feat/kora-KR-SLACK-DM-PANEL

Conversation

@rafe-walker

Copy link
Copy Markdown
Owner

Summary

Operator-facing view of recent Kora ↔ Joshua DM exchanges in the cockpit. Pairs with CC#3's KR-FEAT-SLACK-DM bucket (Feature 5 backend) — ST2 will swap the stub /api/slack-dm/recent body for a real read of ${HERMES_HOME}/slack_dm_log.jsonl. Joshua picked Slack first as his preferred ops channel.

Shape is pinned by the new test suite so the FE shipping in this PR keeps rendering correctly during the cut-over.

What's in here

  • Backend stub: GET /api/slack-dm/recent returning 4 representative messages deliberately spanning inbound (received) + outbound (sent_ok) + filtered_non_joshua so the operator's first look surfaces the filtering posture as well as the happy path. stub: true keeps the FE banner visible.
  • SlackDMPanel.tsx (new): title + stub banner + stats strip (total + by-direction + filtered/error counts) + filter pills (all / inbound / outbound / filtered / errors) + newest-first timeline with direction arrows (←/→) + user-label badge + relative time + status badge (only for non-default statuses — happy-path rows stay clean) + expandable detail view (channel_id, thread_ts, full timestamp, full text, id).
  • Dashboard card feat(KR-3 ST1): iso_node_* typed-graph tool family #9 ("Slack DM ↔ Joshua") with MessageCircle icon. Layout keeps lg:grid-cols-3; row 2 becomes 3+3+3 — the cleanest possible balance, no orphan card. Headline goes destructive when filtered_non_joshua > 0 OR sent_failed > 0 OR handler_error > 0 per spec §2(c).
  • Route /slack-dm + nav entry between /agent-activity and /mcp-clients.

4-layer security contract

This iteration's specific risks: real DM text is arbitrary user-typed content; raw Slack IDs / tokens / signing secrets must never reach the operator's browser.

  1. user_id_label is a LABELjoshua / kora_bot / unknown_user, never a raw Slack user ID (U[A-Z0-9]{8,} shape). Backend test pins per-field AND walk-whole-payload — a future leak via a raw_user_id diagnostic or in the message text gets caught.
  2. channel_id is a STUB label (D_<label>) in v1. Backend test pins the stub format AND explicitly rejects real Slack channel shapes (C/G/D[A-Z0-9]{8,}) so a future flip without hashing/truncation gets caught at the API edge.
  3. text rendered as PLAIN TEXT via React's default child escaping. Frontend pin: SlackDMPanel.tsx never uses dangerouslySetInnerHTML for message bodies (comment-stripped grep so the warning comment doesn't self-trigger).
  4. Walk-the-whole-payload guards: xoxb-/xoxp-/xapp-/xoxa-/xoxr- Slack token shapes, AND 32-char hex runs (Slack signing secret shape) — backend bug or future log-entry edit that leaks creds gets caught before reaching the operator's browser (where it could end up in diag bundles or screenshots).

Test plan

  • tests/kora_cli/test_web_server_slack_dm.py — 15 tests: shape, 4-message stub pin, direction+status diversity, per-entry schema, all 4 security guards, by_direction_24h reconciliation, FE source-pin against dangerouslySetInnerHTML, cron-regression sanity.
  • Full admin-panel regression: 219/219 across 20 suites (skipping test_web_server_mcp_clients.py — PM tracking as task Fix nous refresh token rotation failure in case where api key mint/retrieval fails NousResearch/hermes-agent#269).
  • pnpm tsc --noEmit clean.
  • pnpm build clean.
  • Manual smoke: load /slack-dm, exercise filters, expand rows, verify destructive tone on the dashboard card.

Notes

  • Layout choice: row 2 now has 9 cards → clean 3+3+3 with lg:grid-cols-3. No need to bump columns or push to row 3 — landed the symmetry sweet spot.
  • Convention divergence from spec: spec hints /admin/slack-dm; using flat /slack-dm to match every prior panel in this branch (established by KR-HB-PANEL onward).
  • Newest-first ordering: spec offered the choice; defaulted to newest-first to match every other panel in the branch (recent-activity log semantic). Stable secondary sort on id breaks ties.

🤖 Generated with Claude Code

Operator-facing view of recent Kora ↔ Joshua DM exchanges in the
cockpit. Pairs with CC#3's KR-FEAT-SLACK-DM (Feature 5 backend) —
ST2 will swap the stub /api/slack-dm/recent body for a real read
of ${HERMES_HOME}/slack_dm_log.jsonl. Shape is pinned by the new
test suite so the FE shipping in this PR keeps rendering during
the cut-over.

Joshua picked Slack first as his preferred ops channel.

Single-PR scope:
  * GET /api/slack-dm/recent stub — 4 representative messages
    deliberately spanning inbound (received), outbound (sent_ok),
    and filtered_non_joshua so the operator's first look surfaces
    the filtering posture as well as the happy path. stub:true
    keeps the FE banner visible.
  * SlackDMPanel.tsx — title + stub banner + stats strip (total +
    by-direction + filtered/error counts) + filter pills (all /
    inbound / outbound / filtered / errors) + newest-first
    timeline with direction arrows + user-label badge + relative
    time + status badge (only for non-default statuses, so
    happy-path rows stay clean) + expandable detail view
    surfacing channel_id, thread_ts, full timestamp, full text.
  * Dashboard card #9 (MessageCircle icon — MessageSquare is
    reserved by /sessions). Layout keeps lg:grid-cols-3; row 2
    becomes 3+3+3 — cleanest possible balance, no orphan card.
    Headline goes destructive when filtered_non_joshua>0,
    sent_failed>0, OR handler_error>0 per spec §2(c).
  * Route /slack-dm + nav entry between /agent-activity and
    /mcp-clients.

3-layer security contract (this iteration's specific risks +
extending the established pattern):
  1. user_id_label is a LABEL (joshua / kora_bot / unknown_user)
     — NEVER a raw Slack user ID (U + 8+ alphanumerics shape).
     Backend test pins via _RAW_SLACK_USER_ID regex per-field AND
     walk-whole-payload sweep — a future leak via a "raw_user_id"
     diagnostic field or in the message text gets caught here.
  2. channel_id is a STUB label (D_<label>) in v1. Backend test
     pins the stub shape AND explicitly rejects real Slack channel
     ID shapes (C/G/D + 8+ alphanumerics) so a future flip without
     hashing/truncation gets caught at the API edge.
  3. text content rendered as PLAIN TEXT via React's default child
     escaping. Frontend pin: SlackDMPanel.tsx never uses
     dangerouslySetInnerHTML for message bodies — comment-stripped
     grep so the warning comment doesn't self-trigger.
  4. Walk-the-whole-payload guards: xoxb-/xoxp-/xapp-/xoxa-/xoxr-
     Slack token shapes, AND 32-char hex runs (Slack signing
     secret shape) — catches a backend bug or future log-entry
     edit that leaks creds before they reach the operator's
     browser (where they could end up in diag bundles, screenshots
     etc).

Tests:
  * tests/kora_cli/test_web_server_slack_dm.py — 15 tests covering
    shape, 4-message stub pin, direction+status diversity, per-entry
    schema, all 4 security guards (per-field + whole-payload sweeps
    for user IDs / tokens / signing secrets / channel stub shape),
    by_direction_24h reconciliation, FE source-pin against
    dangerouslySetInnerHTML, cron-regression sanity.
  * Full admin-panel regression: 219/219 across 20 suites
    (skipping the test_web_server_mcp_clients.py failures PM is
    tracking as separate task NousResearch#269).
  * tsc --noEmit + vite build both clean.

Refs: rafe-walker/kora-docs 17_cc_bucket_prompts/KR-SLACK-DM-PANEL_conversation_view_stub.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rafe-walker rafe-walker merged commit c725ab3 into feature/phase2-upgrades May 22, 2026
@rafe-walker rafe-walker deleted the feat/kora-KR-SLACK-DM-PANEL branch May 22, 2026 16:34
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant