Skip to content

feat(discord): native interface integration reference (split series) - #769

Draft
johoja12 wants to merge 15 commits into
Ark0N:masterfrom
johoja12:feat/discord-conversation-interface
Draft

johoja12 wants to merge 15 commits into
Ark0N:masterfrom
johoja12:feat/discord-conversation-interface

Conversation

@johoja12

@johoja12 johoja12 commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Reference PR — being split per maintainer review.

Updated against master 1.50.0. The five flagged CLI checks now use the existing nativeSettingsProfile capability (the session-name prefix has an explicit label-only allowance), and the workspace git caller is in the reviewed gate inventory. Both policy-test files pass. Missing remote python3 now produces an actionable attachment error without surfacing arbitrary SSH stderr. The approved remote-upload exception is recorded in the three requested rule documents; it belongs in the Files part.

Review sequence:

  1. Notifications delivery safety — durable inbound dedup, confirmed outbound parts and no replay of uncertainty.
  2. Full-mode core — channels/threads, native conversation streams, cards and prompt mirroring.
  3. Questions and controls — native questions, models/effort, queues, creation and reconnect.
  4. Activity, Updates, history and guarded workspace Results.
  5. Files — attachments, generated-image galleries, table PNG/CSV and opted-in guarded remote uploads.

The first focused delivery PR is open as #793 (Notifications delivery safety), based directly on master and draft while its independent CI runs. This PR remains the tested integration reference; it is not a request to merge the combined change. The remaining parts will link back here and depend on the earlier pieces.


Discord becomes an opt-in native conversation interface for Codeman. Full mode groups each case into a private channel and gives each session a durable thread; Notifications remains the default.

Conversations and response presentation

  • Stream visible Claude/Codex progress and final answers from exact native transcripts. Conversation is the default view; Quiet is final-only, Tools adds bounded tool output, and Off pauses output.
  • Use native Components V2 response cards with preserved Markdown and code fences. Eligible tables show three records per page, private Next/Previous navigation, a bounded PNG preview and complete CSV export. Read-only controls recover after restart only from verified original responses and owned messages.
  • Attach explicitly linked output files inside their response card, with guarded filename collision handling, size limits and no directory scan. Completed native Codex image-generation events produce silent galleries without requiring a Markdown file link. Exact event hashes, native profile/conversation identity, PNG data and file paths are rechecked. Local reads use bounded Node I/O; SSH helpers use Python 3.
  • Mirror external/web prompts without echoing confirmed Discord-origin prompts. Exact native cursors and provenance are required; identical text alone is insufficient. Queued Claude human prompts are attributed even when execution records are absent.
  • Keep progress, prompts, tool output and supplemental files silent. Finals and genuine questions/errors retain the configured notification policy. Delivery acknowledgements use reactions/the journal rather than redundant prompt cards.

Questions, settings and session controls

  • Forward current Claude single-choice and checkbox questions even when remote hooks are unavailable. Single-choice questions have selectable Discord and web-inbox cards; checkbox/custom-text questions remain terminal-only. Answering checks the exact current question, native conversation, input revision and cursor before one submission. Unconfirmed input is never replayed.
  • Question identity survives wrapped mobile footer text and separator resizing. Claude controls follow the current compaction-chain tail or hook-vouched identity, rather than an inherited conversation guess.
  • Offer model/effort controls, launch defaults, history, files, results, interruption and explicit follow-up queues. Native settings use an exclusive cancellable input lease. Repeated Codex status screens are accepted only with matching live footer evidence; explicit post-change verification remains strict and preserves the conversation.
  • Ordinary replies go directly to the session, including while working. Pending dialogs block unbound input. Explicit queues are bounded, expire, and require unchanged identity/input/configuration plus a freshly verified empty composer before dispatch.
  • Guild-scoped CLI commands/context menus reuse owned destinations and confirmed case-aware quick-start. Concurrent launches reserve numbered case-based names before creation, and the upstream quick-start adapter preserves those names.

Activity, history and workspace results

  • Activity views show Recent, Working, Finished and Needs you from meaningful native events, with owned response/thread links. Cosmetic refreshes do not advance recency. Typing/state names preserve retry-after handling and the shared cosmetic rename budget; rename failures cannot drop alerts.
  • Full-text task Updates supports pagination, Refresh, Load more and bounded downloads from the original prompt through the next prompt. Read-only metadata references can recover controls; state-changing approvals/submissions never recover implicitly.
  • Exact saved transcript cursors continue through bounded pages, including oversized known non-chat tool/image records. Unknown/visible/ambiguous oversized records remain fail-closed. Preserve stream preferences across conversation resets and durable warning recovery. Uncertain sends do not block later native turns; in particular, an uncertain image gallery cannot swallow the later text answer.
  • Results exposes the latest response, linked artifacts and an explicitly unattributed shared-workspace diff. Workspace git uses --no-optional-locks, refuses adopted/container sessions and paths inside registered Docker workspaces, and uses gitSpawnAllowed() / gitMissingError() before local git starts.

Upstream integration and scope

This updates the shipped bridge features through fork cb7db6d3 while retaining the earlier upstream review adaptations: findExactCodexConversation, bounded local Node history reads, real quick-start, bare-Escape interruption, confirmed reconnect against an existing remote agent, shared SSH construction, Notifications HTTP 5xx retries, and cancellation before remote writes. It excludes unrelated fork history, OpenClaw removal, production configuration and the fork's saved Recovery Queue. No production deployment or live production-agent input is part of this update.

The latest workspace-git review is addressed. Current upstream master is still e9b2ecd5 / 1.49.0 and #779 is open, so its git-availability.ts gate is included unchanged as the narrow dependency for this adapter. When #779 lands, its scanner's reviewed caller inventory will also need the new discord-workspace-results.ts call site.

Maintainer policy decisions remain open: incoming SSH uploads are limited to opted-in Full mode under .codeman-discord/ with workspace/component/symlink/size/cancellation guards, proposed as a narrow exception to the existing no-SSH-write policy. Python 3 is required only on SSH hosts. Acceptance of that exception and the combined PR scope remains for the maintainer before merge. Multi-user lifecycle/native controls remain disabled without explicit identity mapping; guild/user allowlists and destination ownership are checked before bridge operations.

Validation

  • 874 tests passed in the broad Discord/native suite. After integration follow-ups, 177 focused Discord/question tests and 89 lifecycle/approval API tests passed.
  • Typecheck, lint, formatting, lockfile, public assets, frontend syntax and the complete pre-push gate passed at e6e7f195.
  • A real isolated remote Claude question was rendered on desktop and phone-sized Chromium. Touch-selecting Blue succeeded through the answer API, retired the card and produced COLOR_SELECTED_Blue in the native conversation, with no browser page errors. Temporary sessions were cleaned up.
  • The general fork-oriented smoke is not fully green: eight scrollback/screenshot-history checks failed, all also reproduced on the previous PR head. Current-head browser console checks passed. These baseline differences are reported separately from the passing Discord tests.
  • GitHub CI is reported separately for the pushed SHA.

Screenshots: desktop question card, mobile question card, table card layout.

Development tracking: johoja12#53 and johoja12#97.

@Ark0N

Ark0N commented Nov 5, 2026

Copy link
Copy Markdown
Owner

Thanks for this, @johoja12. This PR adds an opt-in Full mode that turns Discord into a conversation interface for Codeman sessions, with a private channel per case, a thread per session, native history and live responses, controls, and live activity indicators.

The delivery journal, the prompt-bound controls and the re-checks before every terminal write are careful work. I reproduced the four blockers below with small tests on this branch, and they need fixing before this can merge.

Blockers

  1. src/checkpoint-conversation-identity.ts:255: Codex history can resolve to another tab. The timestamp match against history.jsonl runs before the originator check, and src/discord-history.ts:315 passes no competingSubmitAts. With tab B's prompt 5 s away, tab A resolved to tab B's rollout, and Full mode then posts B's conversation into A's thread. Please delete this module and use findExactCodexConversation from src/agent-codex-identity.ts. That also removes the realpathSync.native call and the unbounded walk on a user-chosen CODEX_HOME (rule fix(cases): bound path probes for linked workspaces and session creation, so an unreachable mount cannot freeze the server #516).
  2. src/web/server.ts:1477: !codeman create ignores the case. CreateSessionSchema has no caseName, so the handler falls back to process.cwd(). On a real WebServer the agent started in the server's working directory with a blank name, and a remote case creates a local session the same way. Please route this through the quick-start path (CLAUDE.md: remote cases go through POST /api/quick-start), and add a test that does not mock create.
  3. src/discord-bridge.ts:2004, :1353, :1323: live output stops for good in three ways. A lost post leaves an uncertain record that blocks every later turn for 7 days, switching !codeman stream verbose to responses leaves a tool cursor that no longer exists, and a cursor outside the 2 MB tail throws on every poll. Please skip an uncertain turn with a visible notice that points to !codeman history, and re-anchor a lost cursor to the latest turn with the same notice.
  4. src/discord-bridge.ts:1950, src/discord-conversation-progress.ts:83, src/discord-case-threads.ts:172: state only grows. sessionDeleted writes discord-case-threads.json on every install, even with the bridge off. The conversation store refuses a 1025th entry and has no delete, and the thread registry stops at 4096. Please return early unless the bridge is enabled in Full mode, delete these records with the session, and age out finished entries.

Should fix before merge

  • src/discord-case-threads.ts:325: a rename that fails with 429 dropped a critical permission alert with zero posts. Session names containing _ * < > or a backtick reach this on every alert, because safeName and DiscordActivity produce different names. Please catch rename failures in ensure, use one name function, and honour retry_after. Library authors report a limit of two renames per ten minutes per channel, and the 30 s throttle does not respect that.
  • src/discord-bridge.ts:1222, :1247, :1773: a fast first answer arrives clipped to 1000 characters as Historical context, and the full alert is deduped. Please treat turns as live when no earlier boundary exists.
  • src/discord-task-controls.ts:80: interrupt sends Ctrl+C, and OpenCode's Ctrl+C is app_exit. Please send a bare ESC, as codeman agent interrupt does.
  • src/discord-session-management.ts:234: !codeman reconnect has no confirmation and no remoteTmuxSessionAlive() check, so it starts a fresh agent when the remote session is gone. Please add both.
  • src/web/server.ts:4553: this early return skips noteRemoteReconnect, and isPaneDead returns false on a tmux error, so auto-reconnect stays stuck in flight. Please clear it here and add a server-level test.
  • test/discord-bridge.test.ts:631: this test moved from 500 to 403 to stay green, and it shows the default mode no longer resends after a 5xx. Thread renames, typing, !codeman file and remote attachment writes also reach Notifications mode. Please keep these in Full mode or document them as default-mode changes.
  • src/remote-hosts.ts:652: buildSshConnectionArgv is a second hand-written ssh builder. Please derive one builder from the other (CLAUDE.md: every ssh command line flows through buildSshConnectionArgs()).
  • Docs: please update CLAUDE.md:134 and :322, docs/architecture-invariants.md:1398, docs/api-reference.md:1556 and :1560 (including the two new routes and the new fields), and docs/wiki/Discord-Bridge.md:19 and :90.
  • src/types/discord.ts:32: onChannelDelete is accepted and stored but never read, so please remove it before it becomes a public field. Please also remove the unused exports in the checkpoint module, the retire path in src/discord-session-channels.ts, and the recovery stubs at src/web/server.ts:581.

Smaller

  • src/discord-bridge.ts:1174: shell and Docker tabs report sync errors while working as designed.
  • src/discord-bridge.ts:1216: /clear undoes !codeman stream off.
  • src/discord-history.ts:300: read the conversation chain tail, since claudeSessionId can be a cwd-keyed correlation.
  • src/discord-history.ts:272: read local transcripts from Node, not python3 plus a full tree walk on every poll.
  • docs/images/discord-*.png: ten screenshots are unreferenced, so please use current ones in the docs or drop them.
  • test/discord-settings.browser.test.ts:10: use a per-run temp directory.
  • Keep @fileoverview first in server.ts, discord-bridge.ts, discord-routes.ts and discord-history.ts, and give new modules the same header.

Open questions

These are not settled by this review: whether writes over ssh are accepted (CLAUDE.md currently rules them out), whether python3 becomes a runtime dependency of Full mode, and whether this lands as one PR or as smaller ones (CONTRIBUTING asks for one change per PR and an issue first for large work).

Next step: please push the blocker and should-fix changes to this branch. This does not merge until those are in and the open questions are settled.

@johoja12

johoja12 commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for the reproductions. Pushed 118f9ccc and d1036b18 to this PR branch (tracking: johoja12#97).

Blockers

  1. Deleted checkpoint-conversation-identity.ts. Local Codex history now uses findExactCodexConversation and its attributed path, with no timestamp fallback after ambiguity. A real fixture reproduces the neighboring-tab/history-timestamp collision and checks that only the owned conversation is read. Claude prefers the hook-vouched chain tail over cwd correlation. Local transcript reads now use bounded Node IO, regular-file/no-follow/nonblocking opens, bounded complete-line tails and guarded cached directory checks; no local Python process or unbounded recursive walk.
  2. Discord creation now invokes the shared quick-start handler, preserving the actual case, SSH host, CLI and group. It assigns a session name and checks the originating configuration/authorization before launch. Real WebServer tests create both local and SSH cases (only native launch/probe IO is replaced), then check the actual Session and its HTTP response using curl. A cancellation test revokes Discord during the remote prerequisite wait and confirms that nothing launches.
  3. Uncertain native delivery skips that turn durably and sends a visible catch-up notice pointing to !codeman history/transcript; later turns continue without replaying the ambiguous chunk. Live reads retain tool rows as cursor anchors when switching back to responses. A cursor outside the bounded tail reanchors once to the latest retained turn with the same notice. Tests cover each path and later successful delivery.
  4. Disabled/Notifications session deletion no longer writes the Full registry. Full deletion closes routing, settles tracked readers, then deletes the session's conversation boundaries across scopes. Finished thread records/tombstones and abandoned boundaries age out; live/off-stream preferences are preserved. Finished boundaries can release capacity rather than blocking the 1025th record. Tests cover retention, capacity, reactivation and deletion during a pending read.

Should-fix findings

  • Cosmetic rename errors are caught after ownership verification, so a 429 cannot drop a critical alert. Activity and thread routing share one name normalizer, including _ * < > and backticks. Actual retry_after is retained; cosmetic name requests share a two-per-ten-minute per-destination budget, and typing retries independently. Tests check a permission alert still posts on a rename 429 and that independent endpoints/budgets behave correctly.
  • A first live response is promoted before an in-flight initial history seed can classify it as clipped historical context. Regression covers a deferred initial history read plus a fast full answer.
  • Interrupt sends bare Escape, matching the agent CLI; no Ctrl+C/app-exit behavior.
  • Reconnect now requires confirmation, unchanged session/configuration evidence and a positive native remote-session existence probe. Owned and attached sessions use their correct tmux socket. Missing/unknown remotes refuse; no replacement agent is launched.
  • The early reconnect return and a rejected pane probe clear watcher bookkeeping without claiming success from isPaneDead() === false. Real WebServer-level regressions cover both and configuration changes during the probe.
  • Restored the Notifications HTTP 500 resend regression. Activity titles/typing, !codeman file and remote attachment writes are Full-only. The remaining default-mode change—durable inbound deduplication and confirmed outbound-part fingerprints, with no blind replay of ambiguous network sends—is now explicit in the user/API/invariant docs. Remote attachment cancellation is rechecked after downloads/probes and inside each queued write.
  • The shell SSH builder now derives from the argv builder, preserving the existing command form and shared port/identity/proxy/jump/options behavior.
  • Updated the requested CLAUDE, architecture, API and wiki sections, including both new routes and mode/category/synchronization fields. Removed onChannelDelete, unused case-channel retirement code and the server recovery stubs.

Smaller findings

Shell/Docker history is intentionally unsupported without sync errors; stream preferences survive conversation resets, including stream-off; local history uses Node; Claude reads the chain tail. Removed all ten unreferenced Discord screenshots. The browser test uses and cleans a per-run temp directory. File overviews precede imports, and the new Discord modules have overview headers.

Validation

  • Final branch: 506 tests passed across 19 files, covering the Discord stack, real server adapters, session routes, Codex attribution, SSH/reconnect and CLI-registry invariants.
  • 8 real-browser Settings tests passed, including desktop/mobile rendering and API calls.
  • Typecheck, lint, format, lockfile, frontend syntax, public assets, browser exclusions, diff checks and the pre-push static gate passed.
  • The broader local sweep caught a CLI-ID branch in my session-naming change; I replaced it with registry capability data and reran that invariant together with the real server tests successfully. The final 506-test run includes the corrected invariant. Upstream CI at d1036b18 is separate and running.
  • No production deployment, live agent input or real Discord messages were used for this follow-up.

Policy/scope decisions before merge

The contract is now explicit: Python 3 is required only on SSH hosts for remote history/file helpers. The SSH write feature is limited to an explicit incoming attachment in opted-in Full mode, saved under .codeman-discord/ with workspace/component/symlink/size guards and cancellation checks. That is a proposed narrow exception to the prior no-SSH-write policy, rather than an existing default-mode behavior. I have kept this as one opt-in conversation-interface PR with supporting commits; please confirm whether that SSH exception and this PR scope are acceptable, or whether you want the remote-upload/lifecycle pieces split before merge.

@johoja12 johoja12 changed the title feat(discord): add a full conversation interface and live activity indicators feat(discord): add native conversations, controls and activity Nov 7, 2026
@johoja12

johoja12 commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

Updated the fork head branch to 1a697d7 to remove the automatic busy-session Send now / Queue next / Cancel chooser, per the user request. Ordinary messages now go directly through the existing input path even while the session is working. Explicit queue controls remain available.

Pending questions/permissions still block unbound replies; multiline input, authorization, destination ownership, inbound deduplication and uncertain-write no-replay behavior remain covered. Existing queued/pending requests are not automatically submitted.

Validation on the upstream-adapted branch: 187 targeted tests passed, plus typecheck, lint, formatting, lockfile, public-asset and complete pre-push checks. The fork production candidate also passed five tests against the compiled modules for direct busy Claude/Codex routing, dialog blocking and false/throw transport uncertainty. GitHub CI remains separate. No native prompts were submitted for testing.

Tracking: johoja12#179; fork merge johoja12#180.

@Ark0N

Ark0N commented Nov 8, 2026

Copy link
Copy Markdown
Owner

Thanks @johoja12, and sorry for the wait. The 1.50.0 release (the macOS app) took the last two days. Thanks for the detailed follow-up on ee6292f3. The move to the shared quick-start handler and the no-replay rule for uncertain sends are the direction I hoped for.

One file still needs work, src/discord-workspace-results.ts, the workspace diff behind the results view. Three things, all in workspaceGit():

1. It starts git without the new git gate. 1.50.0 ships #779: on a Mac without the developer tools, /usr/bin/git is Apple's placeholder, and running it opens macOS's "install the command line developer tools" window. Every git the server starts now goes through gitSpawnAllowed() in src/utils/git-availability.ts, and test/git-spawn-gate.test.ts scans src/ and fails on a direct execFile('git', ...). Once you rebase on 1.50.0 that test fails here. Please call the gate first, and when it says no, answer the way a missing git answers (gitMissingError()), not with an error the user can't act on.

2. It runs git on Docker case workspaces. The scope is { workingDir, remote }, so a Docker session's diff runs on the host, in the bind-mounted folder the container can write to. git diff HEAD runs the repository's clean filters on changed files, and a container can plant one in .git/config, which would then run on the host. The Git status panel refuses any repository at or inside a Docker case workspace for exactly this reason (see "Git status indicator" in CLAUDE.md). Please do the same here, and refuse adopted sessions too: their workingDir is a neutral /tmp, not the session's workspace.

3. --no-optional-locks is missing. Without it git diff can refresh the index and take index.lock, and then the agent's own git commit in the same repository fails with "Unable to create index.lock". The Git panel passes --no-optional-locks on every call; -c core.fsmonitor=false is already there.

On your two scope questions (the narrow SSH write for Discord uploads into .codeman-discord/, and whether to split the PR): I haven't decided those yet and will answer them here separately. They don't need to hold up the three fixes above, which are needed either way.

@johoja12

johoja12 commented Nov 8, 2026

Copy link
Copy Markdown
Contributor Author

Updated the existing fork head to e6e7f19 with the merged Discord features/fixes through fork cb7db6d. The PR description now describes the current implementation and links desktop/mobile screenshots.

Included: paginated table cards with PNG/CSV, selectable Claude question cards, wrapped mobile question handling, inline response files and native generated-image galleries, exact oversized-record catch-up, updates that no longer stall behind a table, reserved case-based launch names, and repeated Codex model-settings verification. The integration retains earlier upstream ownership, cancellation, bounded Node history and no-replay protections. An uncertain gallery cannot swallow a later text answer.

The three latest workspace-diff requests are addressed:

  1. Local git calls use gitSpawnAllowed() and throw gitMissingError() on refusal. Current master is still 1.49 and fix(macos): never run Apple's placeholder git, which opens the developer tools window #779 is open, so this includes fix(macos): never run Apple's placeholder git, which opens the developer tools window #779's gate module unchanged as a narrow dependency. When fix(macos): never run Apple's placeholder git, which opens the developer tools window #779's scanner lands, please include the new discord-workspace-results.ts caller in its reviewed inventory.
  2. Adopted/container sessions are refused. Local paths at or inside registered Docker case workspaces, including symlink aliases, are refused before git runs. An unreadable Docker inventory fails closed.
  3. Every workspace git command uses --no-optional-locks and retains core.fsmonitor=false and existing file guards.

Validation:

  • Broad Discord/native suite: 874 passed. Final focused Discord/question suite: 177 passed; lifecycle/approval API suite: 89 passed.
  • Final typecheck, lint, format, lockfile, public assets, frontend syntax and complete pre-push gate passed.
  • Real isolated remote Claude: rendered the question on desktop, resized to a phone viewport, touch-selected Blue, verified API success/card removal and native COLOR_SELECTED_Blue; no browser page errors. Temporary sessions were cleaned up.
  • Generic fork-oriented browser smoke is not green: eight scrollback/screenshot-history checks failed, and all eight also reproduced on the previous PR head. Current-head console checks passed. The baseline also had a transient early shell-render check failure and missing generated predictive-echo dev assets; those additional baseline failures are not being presented as current-head regressions.
  • GitHub CI is tracked separately for this SHA. No production deployment, production-agent input or diagnostic Discord messages were sent.

Tracking: johoja12#97. The SSH-upload exception and combined PR scope remain maintainer policy decisions before merge.

@Ark0N

Ark0N commented Nov 9, 2026 •

Copy link
Copy Markdown
Owner

Thanks for the update, @johoja12. The workspace diff now goes through the gate, refuses Docker and adopted sessions and passes --no-optional-locks, which covers the three points from my last comment.

CI is red on e6e7f195, though, in two test files. GitHub runs the suite on this branch merged with master, so #779 and its gate test are already part of that run (run 42472911163):

1. test/cli-registry-no-id-branching.test.ts: five new CLI-id branches. The line numbers are from the merged tree:

  • src/discord-native-ui.ts:345: s.mode === 'claude' (prompt suggestion)
  • src/discord-session-management.ts:55: body.mode === 'shell' (the s/w name prefix)
  • src/session.ts:5312: this.mode !== 'claude' (the guarded question controls; line 5259 on your branch)
  • src/web/discord-native-ui-services.ts:61: s.mode !== 'claude' (prompt suggestion reader)
  • src/web/routes/approval-routes.ts:81: session.mode !== 'claude' (native question answer)

The test header explains the two ways out: a capability on the CliEntry, or an ALLOWED_BRANCHES entry with a reason when it is genuinely not CLI behaviour. The three question and suggestion checks are CLI behaviour, so they want a capability (or an existing predicate like hooksAvailableForMode() if it already says what you mean). The name prefix may be a fair ALLOWED_BRANCHES case, since cli.ts and the cron service already have one for a label.

2. test/git-spawn-gate.test.ts: the new site is not in the reviewed list. Master now has src/utils/git-availability.ts (your copy is byte-identical to it, so it drops out on a rebase). The test keeps an inventory of every file that starts git, and discord-workspace-results.ts needs its entry ('discord-workspace-results.ts': 1). That entry is the review step the test exists for, so add it in this PR rather than leaving it for later.

Please rebase on current master (1.50.0) while you're at it, so the branch is tested against what it will merge into.

The two scope questions (the SSH write exception for uploads into .codeman-discord/, and one PR or several) are still with me. I'll answer them here.

@Ark0N

Ark0N commented Nov 9, 2026

Copy link
Copy Markdown
Owner

@johoja12, here are the two scope answers I owed you.

1. Please split it. At 24k lines in 121 files I can't review this as one change with the care the bridge needs, and CONTRIBUTING asks for one change per PR. Your description already falls into parts, so roughly:

  1. Delivery changes that reach Notifications mode (durable inbound dedup, confirmed outbound-part fingerprints, no blind replay). These change the default mode for every bridge user, so they go first and alone.
  2. Full mode core: the case channel and session threads, conversation streaming from the native transcripts (Conversation, Quiet, Tools, Off), the response cards and prompt mirroring.
  3. Questions and controls: question cards (Discord and the web inbox), model and effort controls, queues, create and reconnect.
  4. Activity, Updates, history pages and Results (the workspace diff).
  5. Files: response attachments, image galleries, table cards with PNG and CSV, and incoming uploads, including the remote write below.

The exact cut is yours. What matters is that each PR stands on its own, builds on the one before, and is green in CI by itself. Stacked PRs are fine: open the next one against the previous branch, or as a draft, and say which one it sits on. This PR can stay open as the reference until the pieces land, then close.

2. Yes to the narrow ssh write. An incoming Discord attachment may be written into a remote session's <workspace>/.codeman-discord/ in opted-in Full mode only, with the guards you described (workspace and component checks, symlink refusal, size cap, cancellation re-checks). Nothing else gets written over ssh, and default mode never does. A few conditions:

  • Every ssh command line still goes through buildSshConnectionArgs() and runs under src/remote-ssh-limiter.ts, and paths reach the remote only as shellescaped tokens, like src/remote-files.ts.
  • The PR that adds it writes the exception into the rules it changes: the "no writes over ssh" sentence in the remote sessions paragraph of CLAUDE.md, docs/architecture-invariants.md#remote-sessions-over-ssh and docs/remote-sessions.md, so the next person reading them sees the one exception and its limits.
  • python3 on the remote host is fine for these helpers, but a host without it has to get a clear message in the thread, not a silent failure.
  • It lands in the Files PR, where it can be reviewed on its own.

Thanks for sticking with this. The direction is right, and smaller pieces will get it merged faster.

@johoja12 johoja12 changed the title feat(discord): add native conversations, controls and activity feat(discord): native interface integration reference (split series) Nov 9, 2026
@johoja12
johoja12 marked this pull request as draft November 9, 2026 02:00
@johoja12

johoja12 commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

Updated the published branch to 342d217 against current master 86502b6 / 1.50.0, preserving history rather than force-pushing.

The concrete CI requests are addressed: the four question/suggestion guards use the existing nativeSettingsProfile capability; the shell name prefix has a reasoned label-only allowance; and discord-workspace-results.ts: 1 is in the git-spawn review inventory. The approved SSH-upload exception is recorded in CLAUDE.md, the remote-session architecture rules and docs/remote-sessions.md. Missing remote Python now returns a specific installation message, with stderr kept private.

Validation: 908 broad Discord/native/policy tests passed on the integration; the final Python/file/cancellation subset passed all 12 cases. Typecheck, lint, format, lockfile and the normal pre-push static gate passed. GitHub CI for the new head is running separately.

Split: #793 is the first focused Notifications-mode delivery PR, directly on master. It has 116 local bridge/delivery tests and an isolated API smoke; it remains draft while its own CI runs. I have converted this combined PR to a draft integration reference, as requested. Four subsequent scopes remain: Full-mode core, questions/controls, Activity/history/Results, and Files (including the approved narrow SSH write). They are not being represented as completed splits yet. Tracking is johoja12#97, with part 1 in johoja12#218.

No production deployment or diagnostic Discord messages were sent.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants