Skip to content

feat(cli): add provider-agnostic channel bridge core#109

Closed
cdenneen wants to merge 12 commits intohappier-dev:devfrom
cdenneen:feat/channel-bridge-core
Closed

feat(cli): add provider-agnostic channel bridge core#109
cdenneen wants to merge 12 commits intohappier-dev:devfrom
cdenneen:feat/channel-bridge-core

Conversation

@cdenneen
Copy link

@cdenneen cdenneen commented Mar 4, 2026

Summary

This PR introduces a provider-agnostic channel bridge core for bi-directional session messaging.

It adds the reusable bridge worker and adapter contract, but intentionally does not include any provider-
specific adapter wiring (Telegram/Discord/etc). This keeps the core isolated and reviewable.

What’s Included

  • New core bridge worker:
    • apps/cli/src/channels/core/channelBridgeWorker.ts
  • Core contracts and runtime behavior:
    • channel adapter interface (pullInboundMessages, sendMessage, optional stop)
    • conversation/session binding model
    • in-memory binding store
    • bridge tick execution loop
  • Command routing in core flow:
    • /sessions
    • /attach <session-id-or-prefix>
    • /session
    • /detach
  • Message forwarding behavior:
    • inbound channel messages -> mapped Happier session
    • agent messages -> mapped channel conversation
    • per-binding cursor advancement (lastForwardedSeq)

Tests

  • Added focused tests for core behavior:
    • apps/cli/src/channels/core/channelBridgeWorker.test.ts
  • Covered scenarios:
    • attach + inbound forwarding
    • sessions listing + detach flow
    • outbound agent forwarding + cursor advancement

Out of Scope (Intentional)

  • Provider adapter implementation (Telegram, Discord, Slack, WhatsApp, etc)
  • Daemon wiring / runtime bootstrap for any provider
  • Bridge CLI config surfaces
  • Webhook/polling provider transport details

These are added in the follow-up stacked PR.

Summary by CodeRabbit

  • New Features

    • Provider-agnostic channel bridge: attach/detach sessions, list sessions, send/receive messages via slash commands; binding store with per-binding cursors, inbound deduplication, monotonic cursor advancement, partial-failure tolerance, startup active-session hints, and graceful/idempotent start/stop.
  • Tests

    • Comprehensive test suite exercising command flows (/help, /start, /sessions, /attach, /detach, /session), bidirectional forwarding, deduplication, adapter failure/resilience, authorization/identity warnings, ordering, cursor persistence, and lifecycle behavior.
  • Documentation

    • Added channel-bridge overview and index entry describing runtime model, adapter contract, and tick-loop behavior.

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

Warning

Rate limit exceeded

@cdenneen has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 11 minutes and 55 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c4e65251-2a3f-4d95-9267-f87d7f35a072

📥 Commits

Reviewing files that changed from the base of the PR and between 3bdc6e4 and e315c14.

📒 Files selected for processing (2)
  • apps/cli/src/channels/core/channelBridgeWorker.test.ts
  • apps/cli/src/channels/core/channelBridgeWorker.ts

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a provider-agnostic Channel Bridge: a tick-driven worker that pulls inbound messages from adapters, parses slash-commands, manages channel↔session bindings and cursors, forwards user/agent messages bidirectionally, and includes an in-memory binding store, deduper, tests, and docs.

Changes

Cohort / File(s) Summary
Core Worker
apps/cli/src/channels/core/channelBridgeWorker.ts
New implementation: types/interfaces (conversation refs, inbound messages, actor context, adapters, bindings, deps, store), in-memory binding store, inbound deduper, slash-command parsing and handlers, executeChannelBridgeTick, startChannelBridgeWorker (trigger/stop), cursor persistence, deduplication, adapter lifecycle and stop semantics, authorization hook, and onWarning emissions.
Tests
apps/cli/src/channels/core/channelBridgeWorker.test.ts
New comprehensive test suite exercising tick behavior, adapter/deps harnesses, binding upsert/remove, command flows (/attach, /detach, /sessions, /session, /help, /start), authorization, identity normalization, failure/resilience scenarios, deduplication across ticks, cursor advancement, outbound ordering, and worker stop semantics.
Docs
docs/channel-bridge.md, docs/README.md
Adds channel-bridge documentation and README index entry describing architecture, adapter contract, responsibilities (inbound reception, command parsing, binding management, routing, cursor tracking, forwarding), tick-loop behavior, scope and follow-ups.

Sequence Diagram(s)

sequenceDiagram
    participant Adapter as Channel Adapter
    participant Worker as Channel Bridge Worker
    participant Store as Binding Store
    participant Deps as Session Deps

    rect rgba(100,150,240,0.5)
    Note over Adapter,Worker: Periodic tick pulls inbound messages
    end

    Adapter->>Worker: pullInboundMessages()
    Worker->>Worker: parseSlashCommand / classify message
    alt Slash command (e.g., /attach)
        Worker->>Deps: resolveSessionIdOrPrefix / listSessions
        Worker->>Store: upsertBinding / removeBinding
        Worker->>Adapter: sendMessage (UI hint / failures)
    else User message (bound)
        Worker->>Store: getBinding(ref)
        Worker->>Deps: sendUserMessageToSession(sessionId, message)
    else User message (unbound)
        Worker->>Adapter: sendMessage (no-binding hint)
    end

    Worker->>Store: listBindings()
    loop For each binding
        Worker->>Deps: resolveLatestSessionSeq(sessionId)
        Worker->>Deps: fetchAgentMessagesAfterSeq(sessionId, afterSeq)
        Deps-->>Worker: agent messages
        Worker->>Adapter: sendMessage (agent reply)
        Worker->>Store: updateLastForwardedSeq(ref, newSeq)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main addition: a provider-agnostic channel bridge core for the CLI, which aligns perfectly with the changeset's primary objective.
Description check ✅ Passed The description covers all required template sections: summary (what and why), what's included, tests, and out-of-scope items. However, 'How to test' is missing and 'Checklist' items are not marked.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 4, 2026

Greptile Summary

This PR introduces a provider-agnostic channel bridge core — a robust 31KB worker implementation backed by a comprehensive 57KB test suite. The implementation is the product of extensive multi-round review; all previously identified issues have been fully addressed.

Verified core components:

  • createChannelBridgeInboundDeduper — TTL + capacity-bounded deduplication with throttled pruning to avoid O(N) per-message overhead
  • createInMemoryChannelBindingStore — JSON-tuple-keyed binding store with toNonNegativeInt cursor normalization
  • executeChannelBridgeTick — single tick with deduplication, adapter deduplication by object identity, error handling on all external I/O, and per-row cursor persistence
  • startChannelBridgeWorker — single-flight interval loop with idempotent stop, startup-race microtask yield, and duplicate-adapter deduplication
  • Command routing — /sessions, /attach, /detach, /session, /help, /start with explicit error replies and store operation error handling
  • Inbound/outbound forwarding — timeout-wrapped I/O, per-binding cursor advancement with guard-skip on stale rows, and contextual warnings
  • Documentation — channel bridge core contract with accurate scope and responsibility description

All previously identified risks addressed:

  • Stop/shutdown races eliminated via microtask yield before adapter teardown
  • Cursor monotonicity enforced via toNonNegativeInt guards and per-row persistence
  • Duplicate delivery prevented via shared deduper lifecycle in worker and per-binding row-skip guard
  • Orphaned-binding warnings throttled via per-worker tracking Set
  • All store and dependency operations wrapped in explicit try/catch with contextual error replies
  • Slash-command parsing hardens on malformed input (e.g. /@)
  • Adapter send calls timeout-wrapped to prevent tick stalls on hung provider calls
  • Binding key collision risk eliminated via JSON-tuple encoding

Test suite is comprehensive, condition-based (avoids timing-sensitive fixed sleeps), covers error paths exhaustively, and properly isolates deduper state per test.

Confidence Score: 5/5

  • This PR is safe to merge — it is well-tested, defensively programmed, and all prior review concerns have been systematically addressed.
  • The implementation is the product of extensive review iteration with every previously identified issue (stop races, cursor monotonicity, deduplication, missing-adapter warning throttling, error handling, timeout protection) addressed with corresponding regression tests. Code uses defensive patterns throughout: all external I/O is timeout-wrapped, all store operations are try/catch protected, cursor persistence is incremental per row with overflow guards, and warning deduplication uses stable tracking. Test suite is comprehensive and condition-based, avoiding timing-sensitive failures.
  • No files require special attention.

Last reviewed commit: e315c14

Copy link

@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

🧹 Nitpick comments (2)
apps/cli/src/channels/core/channelBridgeWorker.test.ts (1)

41-203: Add lifecycle coverage for startChannelBridgeWorker (trigger and stop).

executeChannelBridgeTick is covered well, but this PR also introduces a public worker lifecycle API. Add tests for initial trigger behavior, stop idempotency, and adapter stop() propagation.

As per coding guidelines: "Write unit tests for all public functions and components".

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

In `@apps/cli/src/channels/core/channelBridgeWorker.test.ts` around lines 41 -
203, Add unit tests for the new public lifecycle API startChannelBridgeWorker:
write a test that calls startChannelBridgeWorker(...) then calls trigger() once
to ensure the worker runs its first tick (you can assert by spying on
executeChannelBridgeTick or deps called), a test that calls stop() twice to
verify idempotency, and a test that ensures adapter.stop() is called when stop()
is invoked (spy on the adapter harness' stop method). Use the existing
createAdapterHarness(), in-memory store, and deps mocks from the file to locate
where to hook spies and assertions; reference startChannelBridgeWorker, trigger,
stop, and adapter.stop to find the right symbols to exercise and assert.
apps/cli/src/channels/core/channelBridgeWorker.ts (1)

1-368: Add module and exported API JSDoc comments.

This file defines public bridge contracts and complex flow control, but there’s no file header/API JSDoc. Please document responsibilities, expected adapter behavior, and cursor semantics to reduce integration errors.

As per coding guidelines: "Include comprehensive JSDoc comments as file header comments explaining file responsibilities" and "Add JSDoc comments for public APIs and complex logic".

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

In `@apps/cli/src/channels/core/channelBridgeWorker.ts` around lines 1 - 368, Add
a file-level JSDoc header describing the module responsibility (bridging channel
adapters to sessions, command handling, and agent->channel cursor semantics) and
add JSDoc comments for each public exported type/function: ChannelBridgeAdapter
(describe pullInboundMessages/sendMessage/optional stop contract and expected
error handling), ChannelBindingStore and ChannelSessionBinding (explain
lastForwardedSeq meaning and that updateLastForwardedSeq stores the highest seq
forwarded), ResolveSessionIdResult, createInMemoryChannelBindingStore (note
now() usage and normalization of seqs), executeChannelBridgeTick (document flow:
inbound pull -> commands -> forward user messages -> fetchAgentMessagesAfterSeq
where afterSeq is exclusive and returned messages should have seq > afterSeq),
and startChannelBridgeWorker (document tickMs bounds, SingleFlightIntervalLoop
usage and trigger/stop behavior); ensure comments mention expected behavior of
deps methods (resolveLatestSessionSeq, fetchAgentMessagesAfterSeq) and
error/warning callbacks so integrators know contracts and cursor semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/cli/src/channels/core/channelBridgeWorker.test.ts`:
- Around line 46-58: The test currently uses vi.fn() mocks for ChannelBridgeDeps
(listSessions, resolveSessionIdOrPrefix, fetchAgentMessagesAfterSeq, etc.);
replace these mocked spies with lightweight real/test implementations of the
ChannelBridgeDeps interface (for example in-memory or fixture implementations
that implement listSessions, resolveSessionIdOrPrefix, sendUserMessageToSession,
resolveLatestSessionSeq, fetchAgentMessagesAfterSeq, onWarning) so the test
exercises actual behavior instead of mocks, and update assertions to verify real
outcomes; additionally add one or more tests that call the public
startChannelBridgeWorker function (using the same real/test ChannelBridgeDeps)
to cover its lifecycle and observable side-effects (session resolution, message
fetching/sending, and warnings) rather than only testing internal helpers.

In `@apps/cli/src/channels/core/channelBridgeWorker.ts`:
- Around line 247-253: The handler currently trusts event.providerId from
inbound payload which can allow spoofing; normalize and override provider
identity with the adapter's trusted providerId before passing to command
handling and forwarding. Update the call to handleCommand in channelBridgeWorker
to pass event.payload/providerId (or any field named providerId) set to
adapter.providerId, and ensure any later forwarding logic in the same function
(the block around lines 257-279) also replaces uses of event.providerId with
adapter.providerId so routing/binding uses adapter.providerId throughout;
specifically adjust where handleCommand is invoked and where outbound send/bind
logic reads event.providerId.
- Around line 206-216: The current logic uses
deps.resolveLatestSessionSeq(...).catch(() => 0) which can persist a binding
with lastForwardedSeq=0 and replay full history; change the flow in the attach
path so you do not upsertBinding or confirm the attach when seq resolution
fails: call resolveLatestSessionSeq(resolved.sessionId) and if it throws or
returns an invalid cursor, send a failure reply via replyToConversation(adapter,
ref, ...) and return false (or abort) instead of calling store.upsertBinding;
only persist the binding and send the success message after a successful seq
resolution. Ensure you update the code paths around resolveLatestSessionSeq,
store.upsertBinding, and replyToConversation so the binding is only created when
a valid latestSeq is obtained.
- Around line 242-255: The current loop over params.adapters will abort the
whole tick if adapter.pullInboundMessages() or handleCommand() throws; wrap the
call to adapter.pullInboundMessages() in a try/catch so a failing adapter is
logged and the loop continues to the next adapter, and inside the inbound-events
loop wrap handleCommand(...) (and parseSlashCommand if you prefer) in a
try/catch so an error for one event is logged and skipped without stopping
processing of remaining events/adapters; reference the existing symbols:
params.adapters, adapter.pullInboundMessages(), parseSlashCommand(),
handleCommand({ command, event, adapter, store: params.store, deps: params.deps
}) and use the existing logger (e.g. params.deps.logger.error) or console.error
to record the adapter/event and error details.

---

Nitpick comments:
In `@apps/cli/src/channels/core/channelBridgeWorker.test.ts`:
- Around line 41-203: Add unit tests for the new public lifecycle API
startChannelBridgeWorker: write a test that calls startChannelBridgeWorker(...)
then calls trigger() once to ensure the worker runs its first tick (you can
assert by spying on executeChannelBridgeTick or deps called), a test that calls
stop() twice to verify idempotency, and a test that ensures adapter.stop() is
called when stop() is invoked (spy on the adapter harness' stop method). Use the
existing createAdapterHarness(), in-memory store, and deps mocks from the file
to locate where to hook spies and assertions; reference
startChannelBridgeWorker, trigger, stop, and adapter.stop to find the right
symbols to exercise and assert.

In `@apps/cli/src/channels/core/channelBridgeWorker.ts`:
- Around line 1-368: Add a file-level JSDoc header describing the module
responsibility (bridging channel adapters to sessions, command handling, and
agent->channel cursor semantics) and add JSDoc comments for each public exported
type/function: ChannelBridgeAdapter (describe
pullInboundMessages/sendMessage/optional stop contract and expected error
handling), ChannelBindingStore and ChannelSessionBinding (explain
lastForwardedSeq meaning and that updateLastForwardedSeq stores the highest seq
forwarded), ResolveSessionIdResult, createInMemoryChannelBindingStore (note
now() usage and normalization of seqs), executeChannelBridgeTick (document flow:
inbound pull -> commands -> forward user messages -> fetchAgentMessagesAfterSeq
where afterSeq is exclusive and returned messages should have seq > afterSeq),
and startChannelBridgeWorker (document tickMs bounds, SingleFlightIntervalLoop
usage and trigger/stop behavior); ensure comments mention expected behavior of
deps methods (resolveLatestSessionSeq, fetchAgentMessagesAfterSeq) and
error/warning callbacks so integrators know contracts and cursor semantics.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 135e570b-02cd-4832-9f9d-772aa7c7e57f

📥 Commits

Reviewing files that changed from the base of the PR and between f22f06a and cc1706f.

📒 Files selected for processing (2)
  • apps/cli/src/channels/core/channelBridgeWorker.test.ts
  • apps/cli/src/channels/core/channelBridgeWorker.ts

@cdenneen
Copy link
Author

cdenneen commented Mar 4, 2026

@coderabbitai please re-review this PR now that all bot threads have explicit resolution notes and follow-up hardening is complete.

@greptile-apps please run a fresh pass as well.

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

@cdenneen: Understood! I'll kick off a fresh full review pass of PR #109 now that the previous threads have been addressed and the follow-up hardening is in place.

✅ Actions performed

Full review triggered.

Copy link

@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: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/cli/src/channels/core/channelBridgeWorker.ts`:
- Around line 565-570: The current Promise.all over params.adapters can abort if
any adapter.stop() throws; update the shutdown mapping in channelBridgeWorker.ts
to wrap each adapter.stop() call in a try/catch so every adapter.stop() is
attempted even if one fails, log the specific error (including adapter
identifier if available) inside the catch, and return a resolved value so
Promise.all waits for all wrappered stops to complete rather than rejecting on
the first error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 05bcf020-8e3c-440d-9b84-d2c5072a6b1c

📥 Commits

Reviewing files that changed from the base of the PR and between f22f06a and c8e1706.

📒 Files selected for processing (2)
  • apps/cli/src/channels/core/channelBridgeWorker.test.ts
  • apps/cli/src/channels/core/channelBridgeWorker.ts

@cdenneen
Copy link
Author

cdenneen commented Mar 4, 2026

Follow-up clarity pass — here are exact fix locations for the resolved bot items (commit c8e1706f):

I added this map so reviewers can verify each previously-resolved thread against concrete code anchors quickly.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 4, 2026
@cdenneen
Copy link
Author

cdenneen commented Mar 4, 2026

@coderabbitai @greptile-apps follow-up fixes were pushed (commit 3a5d30a) for the latest unresolved threads; please re-review.

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

@cdenneen: Acknowledged! I'll kick off a fresh full review pass of PR #109 to cover the follow-up fixes pushed in commit 3a5d30a2.

✅ Actions performed

Full review triggered.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 4, 2026
@cdenneen
Copy link
Author

cdenneen commented Mar 4, 2026

@coderabbitai @greptile-apps re-review

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

@cdenneen: Acknowledged! Kicking off a fresh full review of PR #109 to cover the latest follow-up fixes.

✅ Actions performed

Full review triggered.

@cdenneen
Copy link
Author

cdenneen commented Mar 4, 2026

@greptile review

cdenneen added a commit to cdenneen/happier-dev that referenced this pull request Mar 4, 2026
Copy link

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/cli/src/channels/core/channelBridgeWorker.test.ts`:
- Around line 700-711: Wrap each test that starts a channel worker with a
try/finally so worker.stop() always runs: for tests that call
startChannelBridgeWorker(...) and then await waitFor(...) / assert on
harness.sent, move assertions into the try block and call await worker.stop() in
the finally block to ensure timers and in-flight work are cleaned up (apply the
same pattern to other tests that startChannelBridgeWorker, reference the worker
variable and its stop() method in the finally).
- Around line 67-73: The test's dedupe verification is passing prematurely
because after calling worker.trigger() the first tick already makes
sentToSession.length === 1 true; update the assertions in createAdapterHarness
tests (and the other occurrences at the noted sites) to deterministically wait
for the second tick before asserting dedupe behavior: after the first
worker.trigger() await the next processing cycle (e.g., call worker.trigger() a
second time or await a Promise that resolves when sentToSession.length >= 2) and
then assert that the duplicate message was ignored (sentToSession length remains
1) or that a dedupe-specific indicator was emitted; reference the harness
functions createAdapterHarness, pushInbound, sent (or sentToSession) and
worker.trigger() when updating each failing assertion to ensure the test
verifies the second tick outcome.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b7334bcb-a690-4b9f-8698-5f4f931f4799

📥 Commits

Reviewing files that changed from the base of the PR and between c8e1706 and 51bb9ea.

📒 Files selected for processing (2)
  • apps/cli/src/channels/core/channelBridgeWorker.test.ts
  • apps/cli/src/channels/core/channelBridgeWorker.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/cli/src/channels/core/channelBridgeWorker.ts

cdenneen added a commit to cdenneen/happier-dev that referenced this pull request Mar 4, 2026
@cdenneen
Copy link
Author

cdenneen commented Mar 4, 2026

Addressed remaining feedback in commit 7fb5b65 (test determinism + lifecycle cleanup guards in channel bridge worker tests).

@coderabbitai review
@greptile review

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

@cdenneen: Acknowledged! Kicking off a fresh full review of PR #109 to cover the test determinism and lifecycle cleanup guard fixes pushed in commit 7fb5b653.

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link

coderabbitai bot commented Mar 4, 2026

@cdenneen: Acknowledged! Kicking off a fresh full review of PR #109 to cover the test determinism and lifecycle cleanup guard fixes in commit 7fb5b653.

✅ Actions performed

Full review triggered.

@cdenneen
Copy link
Author

cdenneen commented Mar 4, 2026

@greptile review

@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

Addressed latest CodeRabbit nitpick in 4771584:

  • external bridge I/O timeout is now configurable via HAPPIER_CHANNEL_BRIDGE_IO_TIMEOUT_MS with safe fallback to 30000ms.

Validated with:

  • yarn --cwd apps/cli vitest run --config vitest.config.ts src/channels/core/channelBridgeWorker.test.ts

@coderabbitai review

@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

@greptile review

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

@cdenneen: Acknowledged! Kicking off a fresh review of PR #109 to cover the latest fix in commit 47715843HAPPIER_CHANNEL_BRIDGE_IO_TIMEOUT_MS configurability with a safe 30000ms fallback. Good to see the targeted test run passing for channelBridgeWorker.test.ts.


🧠 Learnings used
Learnt from: cdenneen
Repo: happier-dev/happier PR: 110
File: apps/cli/src/channels/channelBindingStore.server.test.ts:6-120
Timestamp: 2026-03-04T15:25:01.200Z
Learning: In apps/cli/src/channels/channelBindingStore.server.test.ts, the in-memory KV client helpers (createInMemoryKvClient, createCountingKvClient) are intentional test doubles for ChannelBridgeKvClient by design. The ChannelBridgeKvClient is treated as a true system boundary (network/HTTP KV backend), so local in-memory doubles are acceptable here for deterministic unit tests under CI runtime constraints. Integration coverage against real HTTP/KV backends is planned as a separate test suite, not part of this change set.

Learnt from: CR
Repo: happier-dev/happier PR: 0
File: apps/cli/src/daemon/CLAUDE.md:0-0
Timestamp: 2026-02-16T05:35:08.653Z
Learning: Applies to apps/cli/src/daemon/src/daemon/**/*.ts : Daemon heartbeat loop must run every 60s (or HAPPIER_DAEMON_HEARTBEAT_INTERVAL) to check for version updates via reading package.json from disk and comparing `JSON.parse(package.json).version` with compiled `configuration.currentCliVersion`, and prune dead sessions

Learnt from: CR
Repo: happier-dev/happier PR: 0
File: apps/cli/CLAUDE.md:0-0
Timestamp: 2026-02-16T05:34:34.612Z
Learning: Applies to apps/cli/**/*.test.ts : Use descriptive test names with proper async handling in Vitest tests
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

@coderabbitai review

@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

@greptile review

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

Follow-up fix in 4d2649a:

  • removed unreachable outbound text fallback in channelBridgeWorker ( only)
  • validated with yarn run v1.22.22
    $ /Users/cdenneen/code/workspace/work/happier-dev/node_modules/.bin/vitest run --config vitest.config.ts src/channels/core/channelBridgeWorker.test.ts

RUN v3.2.4 /Users/cdenneen/code/workspace/work/happier-dev/apps/cli

✓ src/channels/core/channelBridgeWorker.test.ts (49 tests) 52ms

Test Files 1 passed (1)
Tests 49 passed (49)
Start at 15:43:12
Duration 5.35s (transform 65ms, setup 6ms, collect 70ms, tests 52ms, environment 0ms, prepare 97ms)

Done in 6.31s.

@coderabbitai review

@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

@greptile review

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

@cdenneen: Acknowledged! Kicking off a fresh review of PR #109 to cover the dead-code removal in commit 4d2649a2 (unreachable outbound text fallback). Good to see all 49 tests green at 52ms.


🧠 Learnings used
Learnt from: CR
Repo: happier-dev/happier PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-21T12:10:41.741Z
Learning: Test suite selection: For tight iteration loops (RED/GREEN), run the smallest relevant subset (single test file, single package). Before handoff and when touching cross-cutting behavior, run the project's full required test run via `yarn test`.

Learnt from: CR
Repo: happier-dev/happier PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-19T14:22:23.401Z
Learning: Applies to **/*.test.{ts,tsx,js,jsx} : Content-only changes (docs, UI copy/wording, formatting, example config files not consumed by runtime, CSS/styling, non-executable templates) do not require new tests. If existing tests fail because they pin copy/formatting, loosen the assertions to check stable behavior instead of exact text.

Learnt from: cdenneen
Repo: happier-dev/happier PR: 110
File: apps/cli/src/channels/channelBindingStore.server.test.ts:6-120
Timestamp: 2026-03-04T15:25:01.200Z
Learning: In apps/cli/src/channels/channelBindingStore.server.test.ts, the in-memory KV client helpers (createInMemoryKvClient, createCountingKvClient) are intentional test doubles for ChannelBridgeKvClient by design. The ChannelBridgeKvClient is treated as a true system boundary (network/HTTP KV backend), so local in-memory doubles are acceptable here for deterministic unit tests under CI runtime constraints. Integration coverage against real HTTP/KV backends is planned as a separate test suite, not part of this change set.

Learnt from: CR
Repo: happier-dev/happier PR: 0
File: apps/cli/CLAUDE.md:0-0
Timestamp: 2026-02-16T05:34:34.612Z
Learning: Applies to apps/cli/**/*.test.ts : Use descriptive test names with proper async handling in Vitest tests

Learnt from: CR
Repo: happier-dev/happier PR: 0
File: apps/ui/CLAUDE.md:0-0
Timestamp: 2026-02-28T06:25:00.032Z
Learning: Applies to apps/ui/sources/**/__tests__/**/*.{test,spec}.{ts,tsx} : Tests exist and should be kept green using Vitest

Learnt from: CR
Repo: happier-dev/happier PR: 0
File: .cursor/rules/edison.mdc:0-0
Timestamp: 2026-02-04T17:46:42.487Z
Learning: When updating code, update ALL related tests (unit, integration, e2e)

Learnt from: cdenneen
Repo: happier-dev/happier PR: 110
File: apps/cli/src/channels/telegram/telegramAdapter.test.ts:17-41
Timestamp: 2026-03-04T15:25:08.440Z
Learning: In apps/cli/src/channels/telegram/telegramAdapter.test.ts, the TelegramApiClient (getMe, getUpdates, sendMessage) is intentionally stubbed with vi.fn mocks. This is an accepted exception to the no-mocking guideline because TelegramApiClient is a true third-party API system boundary requiring external credentials/network. End-to-end Telegram transport validation is tracked separately as an integration suite to avoid CI flakiness.

Learnt from: CR
Repo: happier-dev/happier PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-19T14:22:23.401Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Tests must be deterministic and isolated with no shared global state and no ordering reliance. Do not leave `.skip`, `.todo`, `.only` (or equivalents) committed. Do not use conditional aliases (`const maybeIt = gate ? it : it.skip`) unless the test is an explicit opt-in external probe with a documented gate reason.

Learnt from: CR
Repo: happier-dev/happier PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-19T14:22:23.401Z
Learning: Applies to **/*.{test,spec}.{ts,tsx} : Avoid brittle 'content policing' tests that pin default config values, exact Markdown wording/format/length, or user-facing copy unless copy itself is the product requirement. Prefer stable identifiers, error codes/types, shapes, statuses, and key substrings. Avoid snapshot tests that primarily lock down copy/formatting.

Learnt from: CR
Repo: happier-dev/happier PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-02-21T12:10:41.741Z
Learning: TypeScript: when code changes, run the relevant package `typecheck` lane before handoff.
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 5, 2026
@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

@coderabbitai review

@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

@greptile review

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 5, 2026
@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

@coderabbitai review

@cdenneen
Copy link
Author

cdenneen commented Mar 5, 2026

@greptile review

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cdenneen
Copy link
Author

cdenneen commented Mar 6, 2026

Superseded by #114 (clean rebase + squash replacement). Keeping this PR for history/context only.

@cdenneen
Copy link
Author

cdenneen commented Mar 6, 2026

Closing as superseded by #114 (clean replacement PR).

@cdenneen cdenneen closed this Mar 6, 2026
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.

1 participant