CopilotKit / CopilotKit/outpost

Keep the Teams ack card out of shadow-mode runs

Open
#258 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area: channels area: integrations roadmap: now
Dominant language
TypeScript
Stars
7
Forks
3
Avg merge
7d 16h
Merged PRs (30d)
15

Description

Shadow mode is the switch that lets Outpost run alongside an incumbent without machine-generated text reaching real reporters. The worker's gate covers the AI answer on every platform, and #233 is currently making the predicate itself fail closed rather than fail open on SHADOW_MODE=TRUE.

The Teams bot sits outside that gate. apps/teams-bot/src contains no reference to SHADOW_MODE or isShadowMode at all, and apps/teams-bot/src/handlers/message.ts:120 posts the acknowledgment card at ingest:

if (result.isNewTicket && !result.isOrphanedReply) {
    const card = buildTicketCreatedCard({ ... });
    const reply = MessageFactory.attachment(CardFactory.adaptiveCard(card));
    await context.sendActivity(reply);

Teams is deliberately the only platform that still acknowledges — the comment above that block explains why, and the reasoning holds. But it means Teams is also the only platform whose ingest post the worker's gate structurally cannot cover, because it never goes through the worker.

So on a staging run with shadow mode on: the worker correctly withholds the AI answer, and the reporter has already been told in a real Teams channel that an AI answer is coming. A promise from staging, an answer from production, or a promise and nothing.

Two supporting bits worth fixing at the same time:

  • .env.example:41-42 says the flag "covers EVERY platform (GitHub, Slack, Teams)". Not true today.
  • docs/deployment.md:212 says "When adding any new outbound post path, check SHADOW_MODE before posting" — that's the instruction that produced the three duplicated comparisons #233 is consolidating. Once isShadowMode() lands in shared, that line should name the helper.

Shape of the fix, once #233 merges:

import { isShadowMode } from '@copilotkit/outpost/shared';

if (result.isNewTicket && !result.isOrphanedReply) {
    if (isShadowMode()) {
        console.log(
            `[Teams Bot] Shadow mode — ticket ${result.displayId} created, ack card withheld`,
        );
    } else {
        await context.sendActivity(reply);
    }
}

apps/teams-bot has no shadow-mode test today, so this wants one alongside — ideally using the importOriginal + spread mock so it exercises the real predicate rather than a copy of it.

Depends on #233 for the shared helper. Kept separate so that diff stays about the predicate.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with apps/teams-bot/src/handlers/message.ts:120 and the shared helper from #233, then inspect the existing Teams bot test setup. Add coverage for withholding the acknowledgment card in shadow mode while preserving the normal path, and update .env.example:41-42 and docs/deployment.md:212 to describe the helper. Done means the real predicate is exercised and the Teams ack is absent only when shadow mode is enabled.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend, documentation, testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.