Skip to content

feat(server): let sibling threads exchange messages - #6567

Closed
brian-missionplus wants to merge 1 commit into
pingdotgg:mainfrom
brian-missionplus:feat/thread-relay
Closed

feat(server): let sibling threads exchange messages#6567
brian-missionplus wants to merge 1 commit into
pingdotgg:mainfrom
brian-missionplus:feat/thread-relay

Conversation

@brian-missionplus

@brian-missionplus brian-missionplus commented Aug 14, 2026

Copy link
Copy Markdown

No description provided.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 862b2e42-1611-43f4-adb9-8f286f29dd0d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Aug 14, 2026
commandType: command.type,
detail: "A thread cannot send a message to itself.",
});
}

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.

🟠 High orchestration/decider.ts:947

Relay commands for a deleted source or target are accepted and emit a message/turn request, because requireThreadNotArchived only checks archivedAt. A deleted thread remains in the read model, so explicitly reject deletedAt !== null for both relay endpoints before emitting these events.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/orchestration/decider.ts around line 947:

Relay commands for a deleted source or target are accepted and emit a message/turn request, because `requireThreadNotArchived` only checks `archivedAt`. A deleted thread remains in the read model, so explicitly reject `deletedAt !== null` for both relay endpoints before emitting these events.

@brian-missionplus
brian-missionplus deleted the feat/thread-relay branch August 14, 2026 03:06

@macroscopeapp macroscopeapp 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.

Effect service conventions review: two error-modeling violations in the new thread relay toolkit. Details inline.

Posted via Macroscope — Effect Service Conventions

Comment on lines +13 to +30
export class ThreadRelayError extends Schema.TaggedErrorClass<ThreadRelayError>()(
"ThreadRelayError",
{
code: Schema.Literals([
"source_unavailable",
"target_not_found",
"self_send",
"cross_project",
"query_failed",
"dispatch_failed",
]),
detail: Schema.String,
},
) {
override get message(): string {
return this.detail;
}
}

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.

ThreadRelayError stores an unstructured detail sentence and returns it verbatim as message, so the prose (not the structural attributes) is the only real data, and the code literal is what selects the caller-visible message. It also has no cause, so the underlying ProjectionRepositoryError / engine failure is lost for query_failed and dispatch_failed.

Consider splitting these into distinct Schema.TaggedErrorClass errors with stable structured fields (e.g. threadId, sourceThreadId/targetThreadId, and cause: Schema.Defect() on the two wrapping failures), deriving message from those fields, and exposing the tool failure channel as a Schema.Union — the same shape as PreviewAutomationError in packages/contracts/src/previewAutomation.ts. For example:

export class ThreadRelaySourceUnavailableError extends Schema.TaggedErrorClass<ThreadRelaySourceUnavailableError>()(
  "ThreadRelaySourceUnavailableError",
  { threadId: ThreadId },
) {
  override get message(): string {
    return `Invoking T3 thread '${this.threadId}' is no longer active.`;
  }
}

export class ThreadRelayQueryError extends Schema.TaggedErrorClass<ThreadRelayQueryError>()(
  "ThreadRelayQueryError",
  { threadId: Schema.NullOr(ThreadId), cause: Schema.Defect() },
) {
  override get message(): string {
    return "T3 could not read the current thread catalog.";
  }
}

export const ThreadRelayError = Schema.Union([
  ThreadRelaySourceUnavailableError,
  ThreadRelayQueryError,
  // ...
]);

Posted via Macroscope — Effect Service Conventions

Comment on lines +30 to +40
const queryFailure = () =>
new ThreadRelayError({
code: "query_failed",
detail: "T3 could not read the current thread catalog.",
});

const dispatchFailure = () =>
new ThreadRelayError({
code: "dispatch_failed",
detail: "T3 could not durably accept the thread message.",
});

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.

queryFailure and dispatchFailure are helpers whose only behavior is () => new ThreadRelayError({ ... }), used solely via Effect.mapError (lines 46, 62, 123, 141). Because they ignore their argument, the incoming ProjectionRepositoryError / dispatch failure is discarded entirely rather than preserved as cause, and the same dispatch_failed error covers both UUID/clock acquisition and engine dispatch.

Consider deleting both helpers and constructing the error at each failure boundary with the real error attached, e.g. Effect.mapError((cause) => new ThreadRelayQueryError({ threadId, cause })), so the attributes and the full error chain stay visible at the site that failed.

Posted via Macroscope — Effect Service Conventions

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

Labels

size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants