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
Conversation
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>
This was referenced May 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/recentbody 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
GET /api/slack-dm/recentreturning 4 representative messages deliberately spanninginbound (received)+outbound (sent_ok)+filtered_non_joshuaso the operator's first look surfaces the filtering posture as well as the happy path.stub: truekeeps 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).MessageCircleicon. Layout keepslg:grid-cols-3; row 2 becomes 3+3+3 — the cleanest possible balance, no orphan card. Headline goes destructive whenfiltered_non_joshua > 0ORsent_failed > 0ORhandler_error > 0per spec §2(c)./slack-dm+ nav entry between/agent-activityand/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.
user_id_labelis a LABEL —joshua/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 araw_user_iddiagnostic or in the message text gets caught.channel_idis 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.textrendered as PLAIN TEXT via React's default child escaping. Frontend pin:SlackDMPanel.tsxnever usesdangerouslySetInnerHTMLfor message bodies (comment-stripped grep so the warning comment doesn't self-trigger).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 againstdangerouslySetInnerHTML, cron-regression sanity.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 --noEmitclean.pnpm buildclean./slack-dm, exercise filters, expand rows, verify destructive tone on the dashboard card.Notes
lg:grid-cols-3. No need to bump columns or push to row 3 — landed the symmetry sweet spot./admin/slack-dm; using flat/slack-dmto match every prior panel in this branch (established by KR-HB-PANEL onward).recent-activity logsemantic). Stable secondary sort onidbreaks ties.🤖 Generated with Claude Code