Skip to content

Commit b7b8010

Browse files
tlgimenesclaude
andcommitted
docs(decopilot): clarify queue cancel/mapper semantics + e2e ownership TODO
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d5ac22a commit b7b8010

3 files changed

Lines changed: 14 additions & 1 deletion

File tree

apps/mesh/src/api/routes/decopilot/routes.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,12 @@ export function createDecopilotRoutes(deps: DecopilotDeps) {
843843
const ok = await cancelThreadGateWorkflow(taskId, workflowId);
844844
if (!ok) return c.body(null, 404);
845845

846+
// For a running head, also run the full run-cancel teardown (abort + NATS
847+
// broadcast + run-registry CANCEL). cancelActiveThreadRun re-cancels the
848+
// gate head via cancelThreadGateHead — that re-cancel is idempotent
849+
// (DBOS.cancelWorkflows on an already-cancelled workflow is a no-op), so the
850+
// explicit cancelThreadGateWorkflow above covers the queued case and this
851+
// covers the live run.
846852
if (target.status === "running") {
847853
await cancelActiveThreadRun({
848854
ctx,

apps/mesh/src/dispatch-queue/thread-gate-queue.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface ThreadQueueItem {
1818
enqueuedAt: number;
1919
}
2020

21-
/** Concatenate the text parts of the last user message. Pure + total. */
21+
/** Concatenate the text parts of the last user message, concatenated and trimmed for display. Pure + total. */
2222
export function extractUserMessageText(messages: ChatMessage[]): string {
2323
if (!Array.isArray(messages) || messages.length === 0) return "";
2424
const lastUserIdx = messages.findLastIndex((m) => m?.role === "user");
@@ -51,6 +51,8 @@ export function gateStatusToQueueItem(
5151
workflowId: status.workflowID,
5252
messageId: status.workflowID.slice(prefix.length),
5353
text,
54+
// Caller (listThreadGateQueue) pre-filters to PENDING/ENQUEUED, so the
55+
// else branch is ENQUEUED → "queued".
5456
status: status.status === "PENDING" ? "running" : "queued",
5557
enqueuedAt: status.createdAt,
5658
};

packages/e2e/tests/decopilot-thread-queue.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ test("GET queue 403s for a non-owner", async ({ authedPage, playwright }) => {
161161
await signUpViaApi(otherCtx);
162162

163163
try {
164+
// TODO(e2e-hardening): this proves a NON-MEMBER gets 403 (org-membership
165+
// middleware). The owner-only guard (validateThreadOwnership: created_by !=
166+
// userId) for a non-owner who IS a member of the same org is not yet
167+
// covered — add a second org member via an invite/accept fixture and assert
168+
// 403 on another member's thread when this suite runs against real infra.
164169
// The other user tries to read the owner's thread queue — should 403.
165170
const res = await otherCtx.get(
166171
`/api/${orgSlug}/decopilot/queue/${threadId}`,

0 commit comments

Comments
 (0)