Skip to content

Keep the Teams ack card out of shadow-mode runs #258

Description

@NathanTarbert

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 Treat an unclear SHADOW_MODE as on, instead of posting for real #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.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: channelsChannel go-live: Discord, Slack, GitHub, Teamsarea: integrationsSlack, Teams, Linear, GitHub App, Postmark, Pathfinder, HubSpotroadmap: nowRoadmap horizon: V1 launch critical path / safety

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions