[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-microsoft-ngc-readiness":3,"mdc-q37vwz-key":33,"related-repo-microsoft-ngc-readiness":7717,"related-org-microsoft-ngc-readiness":7725},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":22,"repoUrl":23,"updatedAt":24,"license":25,"forks":26,"topics":27,"repo":28,"sourceUrl":31,"mdContent":32},"ngc-readiness","scan Teams apps for NGC readiness","Scans the Teams platform based app code base. Evaluates the code based on the rules described in the skill. Generates a report on the NGC readiness of the app based on the rules. Trigger phrases include \"Generate a report for NGC readiness\", \"NGC readiness report\", \"Scan for issues related to NGC\" etc. Arguments: FOLDERS to scan. If omitted, scan current folder.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"microsoft","Microsoft","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fmicrosoft.png",[12,16,19],{"name":13,"slug":14,"type":15},"Migration","migration","tag",{"name":17,"slug":18,"type":15},"Microsoft Teams","microsoft-teams",{"name":20,"slug":21,"type":15},"Code Analysis","code-analysis",7,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fteams-platform-skills","2026-04-06T18:37:58.898098",null,2,[],{"repoUrl":23,"stars":22,"forks":26,"topics":29,"description":30},[],"Skills and tools related to Teams extensibility platform","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fteams-platform-skills\u002Ftree\u002FHEAD\u002Fplugins\u002Fngc-readiness\u002Fskills\u002Fngc-readiness","---\nname: ngc-readiness\ndescription: >\n  Scans the Teams platform based app code base.\n  Evaluates the code based on the rules described in the skill.\n  Generates a report on the NGC readiness of the app based on the rules.\n  Trigger phrases include \"Generate a report for NGC readiness\", \"NGC readiness report\", \"Scan for issues related to NGC\" etc.\n  Arguments: FOLDERS to scan. If omitted, scan current folder.\n---\nYou are an expert Microsoft Teams platform engineer. Your job is to scan this codebase and produce a **NGC Channel Readiness Report** — a structured assessment of whether this Teams app is ready to work in shared and private channels (New Gen Channels).\n\n## Argument Parsing\n\nParse `$ARGUMENTS` before starting:\n- The **folders to scan** is `$ARGUMENTS` as a space separated list of folders. If no folder is given, scan the current directory.\n\n---\n\n# NGC Channel Readiness Rules\n\n> **NGC** = New Gen Channels (Shared and Private channels in Microsoft Teams)\n> These rules define what every Teams app must comply with to work correctly in shared and private channels.\n> References:\n> - [Build apps for shared and private channels](https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fmicrosoftteams\u002Fplatform\u002Fbuild-apps-for-shared-private-channels)\n> - [Graph API: channel resource](https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fgraph\u002Fapi\u002Fresources\u002Fchannel?view=graph-rest-1.0)\n> - [Graph API: allMembers](https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fgraph\u002Fapi\u002Fchannel-list-allmembers?view=graph-rest-1.0)\n> - [Graph API: filesFolder](https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fgraph\u002Fapi\u002Fchannel-get-filesfolder?view=graph-rest-1.0)\n> - [Graph API: channel membership change notifications](https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fgraph\u002Fteams-changenotifications-channelmembership)\n> - [Teams SDK (.NET)](https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fdotnet\u002Fapi\u002Fmicrosoft.teams.apps?view=msteams-sdk-dotnet-latest)\n> - [Teams SDK (TypeScript)](https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fjavascript\u002Fapi\u002Fteams-sdk-typescript\u002F?view=msteams-sdk-ts-latest)\n\n## Rule Categories\n\n| ID | Category | Description |\n|----|----------|-------------|\n| [MAN](#man--manifest-rules) | Manifest | App manifest declarations required for NGC |\n| [MEM](#mem--membership-rules) | Membership | Correct use of channel membership APIs |\n| [FS](#fs--file--sharepoint-rules) | File & SharePoint | Channel-scoped file and drive access |\n| [BOT](#bot--bot--messaging-rules) | Bot & Messaging | Bot installation, events, and proactive messaging |\n| [AUTH](#auth--authentication--external-user-rules) | Auth & External Users | Cross-tenant auth and external user handling |\n| [DATA](#data--data-scoping-rules) | Data Scoping | Preventing cross-channel data leaks |\n| [SUB](#sub--graph-subscription-rules) | Graph Subscriptions | Correct subscription configuration for membership changes |\n| [CTX](#ctx--context-api-rules) | Context API | Using getContext() correctly for NGC |\n\n---\n\n## MAN — Manifest Rules\n\n### MAN-1 · Declare `supportsChannelFeatures`\n**Severity: CRITICAL**\n\nAdd `supportsChannelFeatures` with value `tier1` to your app manifest (v1.25+) to make the app available in shared and private channels.\n\n```json\n{\n  \"manifestVersion\": \"1.25\",\n  \"supportsChannelFeatures\": \"tier1\"\n}\n```\n\n**If missing:** The app will not appear in the channel app picker. Users cannot install it in shared or private channels.\n\n**Notes:**\n- `tier1` has no dependency on classic or admin-level access controls.\n- Message extension–only apps scoped to personal use do not require this flag.\n\n---\n\n### MAN-2 · Declare RSC permissions when using channel membership or file APIs\n**Severity: HIGH**\n\nRSC permissions are **conditional** — only required if the app actually calls the relevant Graph APIs. Do not flag these as violations unless the code scan (Step 3) confirms the app uses the corresponding APIs.\n\n- **`ChannelMember.Read.Group`** — required only if the app calls `allMembers`, `members`, or any channel membership Graph API using RSC (not service auth with delegated permissions).\n- **`File.Read.Group`** — required only if the app calls `filesFolder` or accesses channel files via Graph API using RSC.\n\n```json\n{\n  \"authorization\": {\n    \"permissions\": {\n      \"resourceSpecific\": [\n        {\n          \"name\": \"ChannelMember.Read.Group\",\n          \"type\": \"Application\"\n        },\n        {\n          \"name\": \"File.Read.Group\",\n          \"type\": \"Application\"\n        }\n      ]\n    }\n  }\n}\n```\n\n**If missing when required:** Channel membership API calls return 403. File API calls return 403 for RSC-only apps.\n\n**If the app uses service-level auth (client credentials, OBO with delegated Graph permissions) instead of RSC:** These manifest entries are not required.\n\n---\n\n## MEM — Membership Rules\n\n### MEM-1 · Never assume team membership = channel membership\n**Severity: CRITICAL**\n\nDo not use `\u002Fteams\u002F{team-id}\u002Fmembers` or AAD group membership (`\u002Fgroups\u002F{group-id}\u002Fmembers`) to infer who is in a channel.\n\n**What breaks:**\n- Tasks assigned to users who are not in the channel.\n- User pickers showing people who cannot see the channel.\n- Mentions sent to users without channel access.\n- External-only channel members are missed entirely.\n\n**Correct approach:** Always use the channel-scoped `allMembers` API:\n```http\nGET \u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FallMembers\n```\n\n---\n\n### MEM-2 · Use `allMembers` API — not `members` — for channel roster\n**Severity: CRITICAL**\n\n`\u002Fchannels\u002F{id}\u002Fmembers` returns **only directly added members**. Use `allMembers` to include both direct and indirect members (users who joined via a shared team).\n\n```http\nGET \u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FallMembers\n```\n\n**Direct vs. Indirect members:**\n- **Direct:** Explicitly added to the channel (including cross-tenant users).\n- **Indirect:** Members of a team that the channel is shared with. These are identified by the `@microsoft.graph.originalSourceMembershipUrl` annotation pointing to their source team.\n\n**SDK equivalents — scan for VIOLATIONS (`.Members` instead of `.AllMembers`):**\n\n| Language | Violation (direct only) | Correct (all members) |\n|----------|------------------------|----------------------|\n| C# (Graph SDK) | `graphClient.Teams[id].Channels[id].Members.GetAsync()` | `graphClient.Teams[id].Channels[id].AllMembers.GetAsync()` |\n| JavaScript | `client.api('\u002Fteams\u002F...\u002Fchannels\u002F...\u002Fmembers').get()` | `client.api('\u002Fteams\u002F...\u002Fchannels\u002F...\u002FallMembers').get()` |\n| Java | `graphClient.teams().byTeamId(id).channels().byChannelId(id).members().get()` | `...allMembers().get()` |\n| Python | `graph_client.teams.by_team_id(id).channels.by_channel_id(id).members.get()` | `...all_members.get()` |\n| Teams SDK (.NET new) | `context.Api.Teams[id].Channels[id].Members.GetAsync()` | `...AllMembers.GetAsync()` |\n| Teams SDK (TS new) | `app.api.teams(id).channels(id).members.get()` | `...allMembers.get()` |\n\n---\n\n### MEM-3 · Check `doesUserHaveAccess` before acting on member-removed events\n**Severity: HIGH**\n\nWhen a `member removed` notification arrives for an indirect member, the user may still have access via a direct membership path. Always verify before revoking:\n\n```http\nGET \u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FdoesUserHaveAccess(userId='@userid',tenantId='@TenantID',userPrincipalName='@UserPrincipalName')\n```\n\n**If skipped:** Users are incorrectly treated as removed when they still have channel access via a different path.\n\n---\n\n### MEM-4 · Do not assume \"everyone in the team\" can see every channel\n**Severity: HIGH**\n\nPrivate channels are restricted subsets of the team. Shared channels may include members from entirely different teams or tenants. Team-wide \"add all\" or \"notify all\" flows must not use team membership as a proxy for channel visibility. Always rely on `allMembers` API.\n\n```http\nGET \u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FallMembers\n```\n\n---\n\n### MEM-5 · Use `hostTeamGroupID` (not `groupId`) as `team-id` in membership API calls\n**Severity: CRITICAL**\n\nIn a shared or private channel, `getContext()` returns two channel-specific properties:\n- `hostTeamGroupID` — the AAD group ID of the **host team** that owns the channel\n- `hostTenantID` — the tenant ID of the host team\n\nThe plain `groupId` from `getContext()` is the group ID of the team the user is currently navigated under, which in a shared channel may be a **different team** than the one hosting the channel. Using `groupId` as the `team-id` in Graph calls targets the wrong team and returns incorrect or empty membership.\n\n**Incorrect (shared\u002Fprivate channel context):**\n```js\nconst context = await microsoftTeams.app.getContext();\nconst teamId = context.team.groupId; \u002F\u002F ❌ may be the wrong team in shared channels\n```\n\n**Correct:**\n```js\nconst context = await microsoftTeams.app.getContext();\nconst teamId = context.hostTeamGroupID; \u002F\u002F ✅ always the channel's host team\nconst channelId = context.channel.id;\n\u002F\u002F GET \u002Fteams\u002F{teamId}\u002Fchannels\u002F{channelId}\u002FallMembers\n```\n\n**For bots:** Use `turnContext.Activity.GetChannelData\u003CTeamsChannelData>()?.Team?.Id` or `turnContext.Activity.TeamsGetTeamInfo()?.AadGroupId` as the team-id.\n\n**If `groupId` is used instead:**\n- Membership API returns members of the wrong team.\n- Cross-tenant shared channel members are missed entirely.\n- Channel API calls may return 404 because the channel belongs to a different host team.\n\nNote that `hostTeamGroupID` and `hostTenantID` are not applicable for all URLs or URL templates. They are applicable only for Graph API calls, as values read from `getContext()` to be passed in the URL. There are no query parameters or placeholders that app developers need to use with these names.\n\n---\n\n### MEM-6 · Inspect `@microsoft.graph.originalSourceMembershipUrl` to classify direct vs indirect members\n**Severity: MEDIUM**\n\nThe `allMembers` API and indirect membership change notifications include the `@microsoft.graph.originalSourceMembershipUrl` annotation on each member object. Apps must read this annotation to correctly classify members:\n\n- **Direct member:** The annotation URL points back to this channel's own membership endpoint.\n- **Indirect member:** The annotation URL points to a different team (the source team that shares the channel).\n\n```json\n{\n  \"@microsoft.graph.originalSourceMembershipUrl\": \"https:\u002F\u002Fgraph.microsoft.com\u002Fv1.0\u002Ftenants\u002F{tenant}\u002Fteams\u002F{sourceTeamId}\u002Fchannels\u002F{channelId}\u002Fmembers\u002F{memberId}\",\n  \"id\": \"...\",\n  \"roles\": [\"owner\"],\n  \"displayName\": \"Jane Doe\",\n  \"tenantId\": \"...\"\n}\n```\n\n**If not handled:**\n- Apps that remove all \"removed\" users without checking direct\u002Findirect paths may incorrectly revoke access.\n- Deduplication of double-notifications for users who are both direct and indirect members requires this annotation.\n\n---\n\n## FS — File & SharePoint Rules\n\n### FS-1 · Never assume one SharePoint site per team\n**Severity: CRITICAL**\n\nStandard channels share the team's SharePoint site. Shared and private channels each have their **own** dedicated SharePoint site and `driveId`.\n\n**If assumed:**\n- Files are written to the wrong site.\n- File reads return 404 or 403.\n- Content leaks across channel boundaries.\n\n---\n\n### FS-2 · Always resolve `driveId` and `itemId` per channel via `filesFolder`\n**Severity: CRITICAL**\n\nUse this API to get the correct drive root for any channel type:\n\n```http\nGET \u002Fteams\u002F{teamId}\u002Fchannels\u002F{channelId}\u002FfilesFolder\n```\n\nResponse properties to store and reuse:\n- `parentReference.driveId` — the channel's SharePoint driveId\n- `id` — the folderId for the channel's root\n\n**Never** hardcode SharePoint library names or paths (e.g., `\u002FShared Documents\u002F{ChannelName}`). Use this API for all channel types, including standard channels.\n\n**SDK equivalents:**\n\n| Language | Correct usage |\n|----------|--------------|\n| C# (Graph SDK) | `graphClient.Teams[teamId].Channels[channelId].FilesFolder.GetAsync()` |\n| JavaScript | `client.api('\u002Fteams\u002F{id}\u002Fchannels\u002F{id}\u002FfilesFolder').get()` |\n| Python | `graph_client.teams.by_team_id(id).channels.by_channel_id(id).files_folder.get()` |\n| Java | `graphClient.teams().byTeamId(id).channels().byChannelId(id).filesFolder().get()` |\n\n> **Private channel note:** After the private channel SharePoint migration, `filesFolder` returns the root folder's `driveItem`, not a named subfolder. Newly created private channels no longer create a dedicated subfolder — all files go to the root. Do not rely on a named subfolder matching the channel name.\n\n---\n\n### FS-3 · Do not cache a single team-level `driveId` for all channels\n**Severity: CRITICAL**\n\nEach shared\u002Fprivate channel has a distinct `driveId`. A cached team-level driveId will cause all file operations in shared\u002Fprivate channels to target the wrong drive.\n\n**Correct pattern:** Cache driveId **per channelId**, and refresh via `filesFolder` when a new channel is encountered.\n\nSee `FS-2` section for calling patterns.\n\n---\n\n### FS-4 · Use membership-based sharing links, not org-wide links\n**Severity: HIGH**\n\nOrganization-wide or anonymous sharing links exclude external members even if they are channel members.\n\n**Do not use:**\n- Organization-wide links\n- Anonymous links for shared channel content\n\n**Use instead:**\n- Specific-people sharing (`\u002Finvite` API)\n- \"People with existing access\" links\n- Membership-based permissions\n\n```http\nPOST \u002Fdrives\u002F{driveId}\u002Fitems\u002F{itemId}\u002Finvite\n```\n\n---\n\n### FS-5 · Declare `File.Read.Group` RSC permission for RSC apps accessing channel files\n**Severity: HIGH** *(conditional — only applies if the app uses RSC auth for file APIs)*\n\nFor apps using Resource-Specific Consent (RSC) that access channel files via the Graph API, declare the `File.Read.Group` permission. This is the least-privileged RSC application permission for the `filesFolder` API.\n\n**This permission is NOT required** if the app uses service-level auth (client credentials or OBO with delegated Graph permissions) instead of RSC.\n\n```json\n{\n  \"authorization\": {\n    \"permissions\": {\n      \"resourceSpecific\": [\n        {\n          \"name\": \"File.Read.Group\",\n          \"type\": \"Application\"\n        }\n      ]\n    }\n  }\n}\n```\n\n**If missing (RSC apps only):** `GET \u002Fteams\u002F{id}\u002Fchannels\u002F{id}\u002FfilesFolder` returns 403.\n\n---\n\n## BOT — Bot & Messaging Rules\n\n### BOT-1 · Do not assume bot is present in a channel just because it's in the team\n**Severity: CRITICAL**\n\nApps installed at the team level are **not** automatically available in shared or private channels. The app must be explicitly added to each channel.\n\n**If assumed:**\n- Proactive messages to the channel fail with 403.\n- Bot silently drops messages; users see nothing.\n\n**Correct approach:** Use the `channelMemberAdded` event to detect when the bot is added to a channel and trigger per-channel setup logic.\n\n---\n\n### BOT-2 · Listen to `channelMemberAdded` for per-channel bot setup\n**Severity: HIGH**\n\nWhen the bot receives a `channelMemberAdded` event inside `OnConversationUpdateActivityAsync`, use it to:\n- Send a welcome\u002Fsetup message\n- Fetch the channel roster\n- Configure tabs\n- Start scheduled jobs\n\nDo not rely solely on team-level install events (`teamMemberAdded` \u002F uninstall) for per-channel logic.\n\n---\n\n### BOT-3 · Handle `channelshared` and `channelunshared` events\n**Severity: HIGH**\n\nWhen a shared channel is added to or removed from another team, your bot receives a `conversationUpdate` activity with `eventType` = `channelshared` or `channelunshared`.\n\nUse these events to:\n- Update which channels the bot is active in.\n- Stop sending proactive messages to channels the bot is no longer associated with.\n- Trigger a membership refresh via `allMembers`.\n\n```csharp\nvar tcd = turnContext.Activity.GetChannelData\u003CTeamsChannelData>();\nvar eventType = tcd?.EventType?.ToLowerInvariant();\n\nswitch (eventType)\n{\n    case \"channelshared\":\n        \u002F\u002F Refresh membership, update state\n        break;\n    case \"channelunshared\":\n        \u002F\u002F Remove channel from active list\n        break;\n}\n```\n\n**If not handled:** Bot keeps sending to channels it's no longer part of, generating repeated 403 errors and stale state.\n\n---\n\n### BOT-4 · Send welcome messages on `channelMemberAdded`, not only on team install\n**Severity: MEDIUM**\n\nIf welcome\u002Fsetup logic only fires on team install events, shared and private channels never receive it when the bot is first enabled there.\n\n---\n\n### BOT-5 · Use `GetChannelData\u003CSharedChannelChannelData>()` for shared channel event payloads\n**Severity: HIGH**\n\nFor `channelshared` and `channelunshared` events, the full payload (including the list of teams the channel was shared with or unshared from) is only available via `SharedChannelChannelData`, not the base `TeamsChannelData`.\n\n```csharp\n\u002F\u002F ❌ Insufficient — misses SharedWithTeams \u002F UnsharedFromTeams lists\nvar tcd = turnContext.Activity.GetChannelData\u003CTeamsChannelData>();\n\n\u002F\u002F ✅ Correct — gives access to SharedWithTeams and UnsharedFromTeams\nvar extended = turnContext.Activity.GetChannelData\u003CSharedChannelChannelData>();\nvar sharedWith = extended?.SharedWithTeams ?? new List\u003CTeamInfoEx>();\nvar unsharedFrom = extended?.UnsharedFromTeams ?? new List\u003CTeamInfoEx>();\n```\n\nEach `TeamInfoEx` in the list contains `Id`, `Name`, `AadGroupId`, and `TenantId` — use these to update per-team state and handle cross-tenant scenarios.\n\n**If only `TeamsChannelData` is used:** The bot cannot determine which teams the channel was shared with or unshared from, making it impossible to update membership tracking or routing state accurately.\n\n---\n\n### BOT-6 · Use Teams SDK ≥ 2.0.0 (`Microsoft.Teams.SDK` \u002F `@microsoft\u002Fteams-js` v2)\n**Severity: CRITICAL**\n\nNGC-aware APIs — `SharedChannelChannelData`, `AllMembers`, `hostTeamGroupID`, `channel.ownerTenantId`, and the `channelshared`\u002F`channelunshared` event types — were introduced in version 2.0 of both the .NET and JavaScript Teams SDKs. Any version below 2.0 is missing these APIs entirely; calling them against an older SDK silently falls back to incomplete or incorrect behavior.\n\n**For .NET projects — check `*.csproj` \u002F `packages.config` \u002F `Directory.Packages.props`:**\n```xml\n\u003C!-- ❌ Too old — NGC APIs unavailable -->\n\u003CPackageReference Include=\"Microsoft.Teams.SDK\" Version=\"1.*\" \u002F>\n\n\u003C!-- ✅ Minimum required -->\n\u003CPackageReference Include=\"Microsoft.Teams.SDK\" Version=\"2.0.0\" \u002F>\n```\n\n**For JavaScript \u002F TypeScript projects — check `package.json`:**\n```json\n\u002F\u002F ❌ Too old\n\"@microsoft\u002Fteams-js\": \"^1.x.x\"\n\n\u002F\u002F ✅ Minimum required\n\"@microsoft\u002Fteams-js\": \"^2.0.0\"\n```\n\n**If below 2.0.0:**\n- `SharedChannelChannelData` type is absent; `channelshared`\u002F`channelunshared` events cannot be parsed.\n- `hostTeamGroupID` and `channel.ownerTenantId` are not present in the context object.\n- `AllMembers` navigation property does not exist on the Graph SDK fluent client.\n- All BOT-3, BOT-5, CTX-1, CTX-2, and MEM-5 fixes depend on this upgrade being in place first.\n\n---\n\n## AUTH — Authentication & External User Rules\n\n### AUTH-1 · Use `hostTenantId` \u002F `ownerTenantId` for SharePoint token requests\n**Severity: CRITICAL**\n\nExternal (cross-tenant) users reside in their home tenant. When they access SharePoint content in the host channel's site, the `getAuthToken` call must use the **host tenant ID**, not the user's own tenant ID.\n\n**For tabs (JS SDK v2):**\n```js\nconst context = await microsoftTeams.app.getContext();\nconst hostTenantId = context.channel.ownerTenantId;\n```\n\n**For bots (Agent SDK \u002F Bot Framework):**\n```csharp\nvar hostTenantId = turnContext.Activity.Conversation.TenantId;\n```\n\nPass `hostTenantId` in the `tenantId` parameter of `getAuthToken` so SPO grants the correct cross-tenant access.\n\n**If skipped:** External users get token errors or access-denied when opening tab content backed by SharePoint.\n\n---\n\n### AUTH-2 · Do not block users based on unrecognized tenant IDs\n**Severity: HIGH**\n\nShared channels support cross-tenant members. Their `tenantId` will not match the host's `ownerTenantId`. Treating unknown tenant IDs as \"invalid\" accidentally blocks legitimate external collaborators.\n\nDo not assume all users are from the host tenant. Compare `user.tenant.id` (the current user's tenant) with `channel.ownerTenantId` (the host tenant) to detect external users. If they differ, the user is external — not invalid.\n\n```js\nconst context = await microsoftTeams.app.getContext();\nconst isExternal = context.user.tenant.id !== context.channel.ownerTenantId;\n```\n\n---\n\n### AUTH-3 · Detect guest users via `roles` property, not heuristics\n**Severity: MEDIUM**\n\nTo reliably identify guests, check the `roles` field in the `allMembers` response:\n\n```http\nGET \u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FallMembers\n```\n\nFor guests: `\"roles\": [\"guest\"]`\n\nDo not rely on display name patterns, email domain heuristics, or upstream session flags.\n\n---\n\n## DATA — Data Scoping Rules\n\n### DATA-1 · Never include private channel data in team-wide reports or dashboards\n**Severity: CRITICAL**\n\nPrivate channel content is restricted to channel members. Aggregating it into a team-wide view exposes sensitive data to users who do not have access.\n\n**Rule:** Scope all data reads, reports, and analytics to the channel context in which the app is running.\n\n---\n\n### DATA-2 · Do not cross-post or broadcast across channels assuming shared team membership\n**Severity: HIGH**\n\nBroadcasting content from one channel to others, or generating links that span channels, assumes all team members see all channels — which is false for shared and private channels.\n\n**Rule:** Cross-channel operations require explicit permission checks per channel.\n\n---\n\n### DATA-3 · Do not rely on `channelType` or `membershipType` for app behavior logic\n**Severity: CRITICAL**\n\nChannel type values are subject to change as the Teams platform evolves. App logic that branches on `membershipType` or `channelType` will become brittle.\n\n**Correct approach:** Use capability-based APIs (`allMembers`, `filesFolder`, `doesUserHaveAccess`) to determine behavior, not channel-type discrimination.\n\n> **Note:** Reading `membershipType` for **display-only** purposes (e.g., showing a label) is acceptable. The rule applies to branching app logic on these values.\n\n---\n\n## SUB — Graph Subscription Rules\n\n### SUB-1 · Use correct resource path and include `notifyOnIndirectMembershipUpdate=true`\n**Severity: HIGH**\n\nThe correct resource path for subscribing to ALL private and shared channel membership changes in a team is:\n\n```http\n\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers\n```\n\nTo also receive **indirect** membership updates (from shared teams), add both query parameters:\n\n```http\n\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers?notifyOnIndirectMembershipUpdate=true&suppressNotificationWhenSharedUnsharedWithTeam=true\n```\n\nFull subscription request:\n```json\n{\n  \"changeType\": \"created,deleted,updated\",\n  \"notificationUrl\": \"https:\u002F\u002Fyour-webhook\u002Fapi\u002Fnotifications\",\n  \"resource\": \"\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers?notifyOnIndirectMembershipUpdate=true&suppressNotificationWhenSharedUnsharedWithTeam=true\",\n  \"includeResourceData\": true,\n  \"expirationDateTime\": \"2024-09-19T11:00:00.0000000Z\"\n}\n```\n\n**Without `notifyOnIndirectMembershipUpdate=true`:** Only direct membership changes fire. Indirect changes (from shared teams) are silently missed, causing roster drift.\n\n**Without `suppressNotificationWhenSharedUnsharedWithTeam=true`:** Individual per-user notifications fire when a channel is shared\u002Funshared with a large team, causing thousands of events. Always pair this with `SUB-2`.\n\n---\n\n### SUB-2 · Use `sharedWithTeams` subscription for bulk share\u002Funshare events\n**Severity: HIGH**\n\nWhen a shared channel is added to or removed from a large team, per-user notifications can number in the thousands. Subscribe to `sharedWithTeams` for a single share\u002Funshare event, then refresh the full member list via `allMembers`.\n\n```json\n{\n  \"changeType\": \"created,deleted\",\n  \"notificationUrl\": \"https:\u002F\u002Fyour-webhook\u002Fapi\u002Fnotifications\",\n  \"resource\": \"\u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FsharedWithTeams\",\n  \"includeResourceData\": true,\n  \"expirationDateTime\": \"2024-09-19T11:00:00.0000000Z\"\n}\n```\n\nAfter receiving this notification, call `allMembers` to get the current full roster.\n\n---\n\n### SUB-3 · Deduplicate member-added notifications\n**Severity: MEDIUM**\n\nA user who is both a direct and indirect member of a shared channel may trigger duplicate `memberAdded` notifications. Before processing, check whether the user already exists in your roster using `allMembers` and deduplicate using the `@microsoft.graph.originalSourceMembershipUrl` annotation to distinguish the source.\n\n---\n\n### SUB-4 · Include `lifecycleNotificationUrl` for subscriptions with expiry > 1 hour\n**Severity: HIGH**\n\nIf the `expirationDateTime` in a subscription request is more than **one hour** in the future, you must include a `lifecycleNotificationUrl` property. Without it, subscription creation fails with an error.\n\n```json\n{\n  \"changeType\": \"created,deleted,updated\",\n  \"notificationUrl\": \"https:\u002F\u002Fyour-webhook\u002Fapi\u002Fnotifications\",\n  \"lifecycleNotificationUrl\": \"https:\u002F\u002Fyour-webhook\u002Fapi\u002Flifecycle\",\n  \"resource\": \"\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers?notifyOnIndirectMembershipUpdate=true\",\n  \"expirationDateTime\": \"2024-09-19T11:00:00.0000000Z\"\n}\n```\n\n**If missing:** Subscription creation requests with expiry > 1 hour will be rejected by the platform.\n\n---\n\n## CTX — Context API Rules\n\n### CTX-1 · Use `hostTeamGroupID` and `hostTenantID` from `getContext()` for Graph calls\n**Severity: HIGH**\n\nFor shared and private channels, `getContext()` returns two new properties:\n- `hostTeamGroupID` — the group ID of the team that hosts the channel\n- `hostTenantID` — the tenant ID of the host team\n\nUse these (not the bare `groupId`) when calling Graph APIs for channel membership, file access, or tenant comparison.\n\n> See also `MEM-5` for the specific membership API implication: `hostTeamGroupID` must be used as the `{team-id}` parameter in all `\u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002F...` Graph API calls.\n\n---\n\n### CTX-2 · Use `channel.ownerTenantId` for cross-tenant SharePoint access\n**Severity: HIGH**\n\nWhen calling `getAuthToken` for SharePoint resources from a tab, use `context.channel.ownerTenantId` as the `tenantId` parameter. This ensures tokens are scoped to the host tenant where the SharePoint site lives.\n\n---\n\n# Scanning Instructions\n\n## Step 1 — Short Circuit: Check if the app has channel scope\n\n**Before doing any deep scan**, locate `manifest.json` (or files under `appPackage\u002F`).\n\nCheck whether **any** element in the manifest has a `\"scopes\"` array that contains `\"team\"`. This includes `configurableTabs`, `staticTabs`, `bots`, `composeExtensions`, etc.\n\n- **If NO element has `\"team\"` in its `scopes`:** The app has no channel installation surface (personal-only or groupChat-only). NGC rules do not apply. **Stop here** and report: ✅ **Channel Ready — NGC Not Applicable** with a note that the app has no team\u002Fchannel scope and therefore does not need NGC compliance.\n\n- **If ANY element has `\"team\"` in its `scopes`:** The app can be installed in channels. Continue to Step 2.\n\n- **If `manifest.json` is not found:** Issue a warning that manifest is not available (may be hosted on CDN). Proceed with code scanning and note that manifest must be verified separately.\n\n## Step 2 — Manifest Check (2 mandatory items + deferred RSC check)\n\nRead `manifest.json` and check **only** the items below. Do not evaluate any other manifest section. Ignore all URLs in the manifest.\n\n1. **`manifestVersion`** — must be `\"1.25\"` or higher. Flag as **CRITICAL** if lower or missing.\n2. **`supportsChannelFeatures`** — must be present with value `\"tier1\"`. Flag as **CRITICAL** if missing or wrong value.\n\n**RSC permissions (`ChannelMember.Read.Group`, `File.Read.Group`) are NOT checked here.** They are conditional on what the code actually does and are evaluated during the code scan in Step 3:\n- `ChannelMember.Read.Group` is checked inside the **Membership APIs** scan section.\n- `File.Read.Group` is checked inside the **File & SharePoint** scan section.\n\nDo not flag RSC permissions as violations in this step.\n\nDo not flag any other manifest fields as violations.\n\n## Step 3 — Code Scan\n\nUsing the rules above as evaluation criteria, systematically search the codebase for evidence of compliance or violation. Use targeted searches across all relevant file types.\n\n> **Construct applicability principle:** Every rule category below is conditional on the app actually using that construct. Before scanning a category, first confirm the construct is present. If it is absent, **skip the entire category** and note it as \"not applicable\" in the report — do not flag phantom violations for code that doesn't exist. The applicability check for each category is listed under its **\"Skip if\"** line.\n\n---\n\n### Membership APIs\n**Skip if:** No Graph API calls to teams, channels, groups, or membership endpoints exist anywhere in the codebase (search for `graph`, `teams`, `channels`, `members`). If absent, skip all MEM rules.\n\n**Flag as violations:**\n- `GetGroupMembersAsync` \u002F `\u002Fgroups\u002F{id}\u002Fmembers` used for channel access decisions\n- `.Members.GetAsync()` — C# Graph SDK, returns direct only\n- `.members().get()` — Java Graph SDK, returns direct only\n- `.members.get()` — Python Graph SDK, returns direct only\n- `client.api('\u002Fteams\u002F...\u002Fchannels\u002F...\u002Fmembers').get()` — JavaScript, returns direct only\n- `\u002Fteams\u002F{id}\u002Fmembers` — team-level member list, not channel-scoped\n\n**Look for correct usage:**\n- `.AllMembers.GetAsync()` — C# Graph SDK ✅\n- `.allMembers().get()` — Java Graph SDK ✅\n- `.all_members.get()` — Python Graph SDK ✅\n- `client.api('\u002Fteams\u002F...\u002Fchannels\u002F...\u002FallMembers').get()` — JavaScript ✅\n- `\u002Fteams\u002F{id}\u002Fchannels\u002F{id}\u002FallMembers` in raw HTTP calls ✅\n\n**Also search for:**\n- `doesUserHaveAccess` — note if used or absent on member-removed events\n- `@microsoft.graph.originalSourceMembershipUrl` — note if absent (Bucket 3 item)\n- `groupId` used as `team-id` in Graph calls — flag if `hostTeamGroupID` is not used instead\n- `hostTeamGroupID` — confirm it's read from context and passed to Graph APIs\n\n**RSC manifest check (MAN-2 — only if membership APIs are in use):**\nIf the app calls `allMembers`, `members`, or any channel membership Graph API, check whether the app uses RSC (Resource-Specific Consent) or service-level auth (client credentials \u002F OBO with delegated Graph permissions):\n- If using **RSC**: flag as **HIGH** if `ChannelMember.Read.Group` is absent from `authorization.permissions.resourceSpecific` in the manifest.\n- If using **service-level auth**: no manifest RSC entry is needed — do not flag.\n- If auth method is unclear: note it as a Bucket 2 observation.\n\n---\n\n### File & SharePoint\n**Skip if:** No SharePoint, OneDrive, or drive-related code exists (search for `drive`, `sharepoint`, `filesFolder`, `Shared Documents`, `driveId`). If absent, skip all FS rules.\n\n**Flag as violations:**\n- Hardcoded `\u002FShared Documents\u002F` paths in SPO URLs\n- `driveId` cached at team level without `channelId` as key\n- Org-wide or anonymous link generation patterns\n\n**Look for correct usage:**\n- `filesFolder` in API calls — confirms FS-2 compliance ✅\n- `.FilesFolder.GetAsync()` — C# SDK ✅\n- `parentReference.driveId` stored per-channel ✅\n- `\u002Fdrives\u002F{driveId}\u002Fitems\u002F{itemId}\u002Finvite` — membership-based sharing ✅\n\n**RSC manifest check (MAN-2 \u002F FS-5 — only if file APIs are in use):**\nIf the app calls `filesFolder` or accesses channel files via Graph API, check whether it uses RSC or service-level auth:\n- If using **RSC**: flag as **HIGH** if `File.Read.Group` is absent from `authorization.permissions.resourceSpecific` in the manifest.\n- If using **service-level auth**: no manifest RSC entry is needed — do not flag.\n- If auth method is unclear: note it as a Bucket 2 observation.\n\n---\n\n### Bot & Agent SDK Events\n**Skip if:** The manifest has no `bots` element. If no bot is registered, skip all BOT rules including BOT-6.\n\n**SDK version check (BOT-6 — applies to all bot projects):**\n- Search `*.csproj`, `Directory.Packages.props`, and `packages.config` for `Microsoft.Teams.SDK`. **Flag as CRITICAL** if the resolved version is below `2.0.0` (e.g., `1.*`, `0.*`, or any pre-release below 2.0).\n- Search `package.json` for `@microsoft\u002Fteams-js`. **Flag as CRITICAL** if the version is below `2.0.0` (e.g., `^1.x.x`).\n- If neither package is present as a direct dependency, note it and skip BOT-6.\n\n**Flag as violations (only if bot does proactive messaging or has per-channel state):**\n- `OnConversationUpdateActivityAsync` present but no handling for `channelshared`\u002F`channelunshared` eventTypes\n- `GetChannelData\u003CTeamsChannelData>()` used alone for shared channel events without `SharedChannelChannelData`\n- Bot setup logic only in `teamMemberAdded` with no `channelMemberAdded` equivalent\n- Proactive message sending without verifying the bot is in the target channel\n\n**Note: Messaging-extension-only bots** (link unfurling, search commands with no proactive messaging or per-channel state) are **not subject to BOT-1 through BOT-5** — these rules only apply to bots that send proactive messages or maintain per-channel install state. BOT-6 (SDK version) applies to all bot projects regardless.\n\n---\n\n### Authentication & External Users\n**Skip if:** No authentication flows exist — no `getAuthToken`, `AcquireToken`, MSAL, OBO, or similar token-acquisition code found anywhere in the codebase. If absent, skip all AUTH rules.\n\n**Flag as violations:**\n- Code that rejects users based on unrecognized\u002Fnon-matching tenant IDs (hardcoded tenant allowlists, blanket denial of `IsEnterpriseGuest` in shared channel contexts)\n- `getAuthToken` called without passing `tenantId` when SharePoint is involved\n\n**Note for Bucket 2 (suggestions, not violations):**\n- Apps that restrict functionality for external\u002Fguest users based on session flags rather than Graph `roles` — the app may intentionally not support external users, but it's worth suggesting they evaluate this per channel type\n\n---\n\n### Data Scoping\n**Skip if:** No multi-channel aggregation, reporting, broadcast, or cross-channel post patterns exist, AND no `membershipType`\u002F`channelType`\u002F`MembershipType`\u002F`ChannelType` identifiers appear anywhere in the codebase. If absent, skip DATA rules.\n\n- Search for analytics, reporting, dashboard, aggregate query code — check if it reads from multiple channels without access gating\n- Search for broadcast or cross-post patterns\n- **Flag as Bucket 1 violation (DATA-3 CRITICAL):** `membershipType` or `channelType` used in `if`\u002F`switch`\u002F`case` branching to select different code paths, different API calls, or different access decisions. Quote the switch\u002Fcase block. The fix is to replace all branches with a single `allMembers` (or `filesFolder`) call that works for every channel type uniformly.\n  - Exception: reading the value for display-only purposes (e.g., showing a label \"Private channel\") is acceptable and should NOT be flagged.\n\n---\n\n### Graph Subscriptions\n**Skip if:** No Graph change notification or subscription code exists (search for `subscriptionUrl`, `notificationUrl`, `changeType`, `\u002Fsubscriptions`). If absent, skip all SUB rules.\n\n**Flag as violations:**\n- Subscription resource pointing to `\u002Fchannels\u002F{id}\u002Fmembers` — wrong endpoint\n- Missing `notifyOnIndirectMembershipUpdate=true` in membership subscription resource\n- Missing `lifecycleNotificationUrl` when `expirationDateTime` is set to more than 1 hour out\n- No `sharedWithTeams` subscription when the app handles bulk membership events\n\n---\n\n### Context API\n**Skip if:** No `getContext`, `microsoftTeams.app.getContext`, or Teams JS SDK context calls exist in the frontend code. If absent, skip all CTX rules.\n\n- `hostTeamGroupID` — confirm it's read from context and passed to Graph APIs\n- `hostTenantID` — confirm it's read for tenant comparisons\n- `channel.ownerTenantId` — confirm it's used for SPO token scoping\n\n---\n\n# Report Format\n\nProduce the following structured report as an MD file in the root of the repo.\n\n---\n\n# NGC Channel Readiness Report\n**App:** [name or root directory of the scanned codebase]\n**Scanned:** [today's date]\n\n---\n\n## Overall Readiness\n\nState one of the following verdicts, bold and prominent:\n\n| Verdict | Criteria |\n|---------|----------|\n| ✅ **Channel Ready** | Zero confirmed violations. Manifest has `supportsChannelFeatures: tier1` and correct manifest version. |\n| ✅ **Channel Ready — NGC Not Applicable** | App has no `\"team\"` scope in any manifest element. NGC rules do not apply. |\n| ⚠️ **Needs Minor Changes** | No CRITICAL violations. A few HIGH rules need attention. Estimated effort of 2 to 3 days. |\n| 🔴 **Needs Major Changes** | One or more CRITICAL violations. Core APIs or manifest need rework. Estimated effort 1+ weeks. |\n\nFollow the verdict with 2–4 sentences explaining *why* the app is in this state, naming the dominant failure categories.\n\n---\n\n## Bucket 1 — Confirmed Violations\n\n> These are blatant API misuse or missing declarations that will **certainly** break the app in shared or private channels. Each item must be fixed before the app is NGC-ready.\n\n**Grouping rule — organize by code path, not by rule ID.**\nGroup all violations that touch the same file or the same logical code area into a single entry. List every applicable rule ID in the header. This makes it easy for a developer to open one file and see all the issues they need to fix in one place. Ordering: CRITICAL entries first, then HIGH.\n\n**When multiple membership API calls are wrong in the same file** (e.g., `GetGroupMembersAsync`, `GetChannelMembersAsync`, and `GetChannelMemberAsync` all need to become `allMembers`), describe them all together in one entry rather than creating a separate entry for each call site.\n\nFor each entry:\n\n```\n### [N]. [File or Logical Code Area] — [Short Violation Title]\n**Severity:** CRITICAL | HIGH  (use the highest severity among all violations in this group)\n**Rules violated:** [e.g., MEM-1, MEM-2, DATA-3]\n**Files:** `path\u002Fto\u002Ffile:line`  (list multiple lines if violations span several locations in the same file)\n\n**What's wrong:**\n[Describe all violations in this code area together. Quote each specific call or pattern. Explain why each fails in shared\u002Fprivate channels. If multiple similar calls have the same root cause (e.g., all need allMembers), state the root cause once and enumerate the affected call sites.]\n\n**Fix:**\n[Consolidated fix addressing all violations in this entry. Correct API, code pattern, manifest field, or SDK method. Include a short snippet if it clarifies the fix.]\n\n**Impact if unresolved:**\n[One sentence on the concrete user-facing or security consequence in shared\u002Fprivate channels.]\n```\n\nIf there are zero violations total, state: *No confirmed violations found.*\n\n---\n\n## Bucket 2 — Suggestions & Observations\n\n> These are patterns that **may or may not** be issues depending on the app's intended behavior. The app could have valid reasons for these patterns — they are flagged here for review, not as required fixes.\n\nUse a concise list format. For each item:\n\n```\n**[Rule ID] — [Short title]**\n`path\u002Fto\u002Ffile:line`\n[One sentence describing the pattern and why it's worth reviewing. End with a specific suggestion for what to check or consider.]\n```\n\nExamples of what belongs here:\n- App checks for guest or enterprise guest users and restricts functionality — valid if intentional, but worth reviewing per channel type (shared channels are designed for external collaboration)\n- App reads `groupId` from context and passes to a backend — may be correct if the backend remaps it; verify the backend uses host team ID for channel-scoped Graph calls\n\n---\n\n## Bucket 3 — Futuristic \u002F Migration Prep\n\n> These are **not current violations** — the code does not use these APIs yet. They are changes that will be required when migrating to NGC-compliant patterns, or that would improve correctness and resilience once the Bucket 1 fixes are in place.\n\nUse a concise list format. For each item:\n\n```\n**[Rule ID] — [Short title]**\n[One sentence describing what needs to be added or changed, and when it becomes relevant (e.g., \"Required when adopting allMembers API\").]\n```\n\nExamples of what belongs here:\n- Add `ChannelMember.Read.Group` RSC permission to manifest — required when moving from service-auth Graph calls to RSC-based channel membership APIs\n- Extract `hostTeamGroupID` from `getContext()` and pipe to backend — required alongside any allMembers migration\n- Inspect `@microsoft.graph.originalSourceMembershipUrl` on allMembers responses — needed after allMembers adoption to correctly classify direct vs indirect members and avoid double-counting\n\n---\n\n## Effort Estimate (Bucket 1 only)\n\n| Area | Violations | Effort |\n|------|-----------|--------|\n| Manifest | | |\n| Membership APIs | | |\n| File \u002F SharePoint | | |\n| Bot Events | | |\n| Auth | | |\n| Data Scoping | | |\n| Graph Subscriptions | | |\n| Context API | | |\n| **Total** | | **[range]** |\n\n**Complexity driver:** [The single biggest source of effort in one sentence]\n\nGenerate the report as an MD file in the root folder.",{"data":34,"body":35},{"name":4,"description":6},{"type":36,"children":37},"root",[38,54,61,75,99,103,110,199,205,414,417,423,436,444,464,573,583,591,609,612,618,626,638,692,994,1004,1014,1017,1023,1029,1036,1057,1065,1088,1105,1121,1124,1144,1151,1176,1189,1197,1228,1252,1435,1438,1452,1459,1472,1486,1496,1499,1505,1512,1524,1537,1540,1570,1577,1590,1621,1661,1669,1787,1795,1935,1961,1976,1994,2020,2023,2036,2044,2062,2085,2290,2298,2311,2314,2320,2326,2333,2352,2360,2378,2381,2406,2413,2418,2432,2437,2461,2479,2487,2572,2600,2603,2616,2623,2635,2659,2672,2675,2681,2688,2693,2701,2714,2722,2748,2762,2765,2778,2793,2811,2821,3012,3029,3032,3038,3044,3051,3063,3070,3083,3100,3103,3116,3123,3143,3166,3179,3182,3203,3210,3243,3248,3272,3375,3384,3387,3400,3407,3412,3415,3429,3436,3469,3530,3573,3590,3593,3615,3622,3668,3697,3745,3760,3855,3863,3920,3923,3929,3950,3957,3977,3985,4079,4087,4101,4127,4136,4139,4145,4152,4171,4191,4321,4324,4337,4344,4363,4376,4387,4392,4395,4401,4407,4414,4419,4429,4432,4438,4445,4450,4459,4462,4483,4490,4508,4536,4563,4566,4572,4584,4591,4596,4610,4622,4636,4641,4831,4847,4870,4873,4887,4894,4912,5094,5106,5109,5115,5122,5149,5152,5166,5173,5199,5393,5402,5405,5411,5436,5443,5455,5477,5489,5528,5531,5544,5551,5577,5580,5586,5592,5618,5675,5754,5760,5779,5838,5861,5895,5900,5905,5911,5916,5943,5946,5951,5989,5997,6072,6080,6137,6145,6202,6225,6278,6281,6286,6329,6336,6375,6382,6427,6443,6485,6488,6494,6510,6518,6620,6628,6694,6711,6714,6720,6743,6750,6783,6791,6806,6809,6814,6850,6929,6932,6937,6973,6980,7037,7040,7045,7068,7100,7103,7109,7114,7117,7122,7147,7150,7156,7161,7268,7280,7283,7289,7304,7314,7351,7356,7366,7376,7379,7385,7400,7405,7414,7419,7439,7442,7448,7463,7467,7476,7480,7524,7527,7533,7692,7706,7711],{"type":39,"tag":40,"props":41,"children":42},"element","p",{},[43,46,52],{"type":44,"value":45},"text","You are an expert Microsoft Teams platform engineer. Your job is to scan this codebase and produce a ",{"type":39,"tag":47,"props":48,"children":49},"strong",{},[50],{"type":44,"value":51},"NGC Channel Readiness Report",{"type":44,"value":53}," — a structured assessment of whether this Teams app is ready to work in shared and private channels (New Gen Channels).",{"type":39,"tag":55,"props":56,"children":58},"h2",{"id":57},"argument-parsing",[59],{"type":44,"value":60},"Argument Parsing",{"type":39,"tag":40,"props":62,"children":63},{},[64,66,73],{"type":44,"value":65},"Parse ",{"type":39,"tag":67,"props":68,"children":70},"code",{"className":69},[],[71],{"type":44,"value":72},"$ARGUMENTS",{"type":44,"value":74}," before starting:",{"type":39,"tag":76,"props":77,"children":78},"ul",{},[79],{"type":39,"tag":80,"props":81,"children":82},"li",{},[83,85,90,92,97],{"type":44,"value":84},"The ",{"type":39,"tag":47,"props":86,"children":87},{},[88],{"type":44,"value":89},"folders to scan",{"type":44,"value":91}," is ",{"type":39,"tag":67,"props":93,"children":95},{"className":94},[],[96],{"type":44,"value":72},{"type":44,"value":98}," as a space separated list of folders. If no folder is given, scan the current directory.",{"type":39,"tag":100,"props":101,"children":102},"hr",{},[],{"type":39,"tag":104,"props":105,"children":107},"h1",{"id":106},"ngc-channel-readiness-rules",[108],{"type":44,"value":109},"NGC Channel Readiness Rules",{"type":39,"tag":111,"props":112,"children":113},"blockquote",{},[114,124],{"type":39,"tag":40,"props":115,"children":116},{},[117,122],{"type":39,"tag":47,"props":118,"children":119},{},[120],{"type":44,"value":121},"NGC",{"type":44,"value":123}," = New Gen Channels (Shared and Private channels in Microsoft Teams)\nThese rules define what every Teams app must comply with to work correctly in shared and private channels.\nReferences:",{"type":39,"tag":76,"props":125,"children":126},{},[127,139,149,159,169,179,189],{"type":39,"tag":80,"props":128,"children":129},{},[130],{"type":39,"tag":131,"props":132,"children":136},"a",{"href":133,"rel":134},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fmicrosoftteams\u002Fplatform\u002Fbuild-apps-for-shared-private-channels",[135],"nofollow",[137],{"type":44,"value":138},"Build apps for shared and private channels",{"type":39,"tag":80,"props":140,"children":141},{},[142],{"type":39,"tag":131,"props":143,"children":146},{"href":144,"rel":145},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fgraph\u002Fapi\u002Fresources\u002Fchannel?view=graph-rest-1.0",[135],[147],{"type":44,"value":148},"Graph API: channel resource",{"type":39,"tag":80,"props":150,"children":151},{},[152],{"type":39,"tag":131,"props":153,"children":156},{"href":154,"rel":155},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fgraph\u002Fapi\u002Fchannel-list-allmembers?view=graph-rest-1.0",[135],[157],{"type":44,"value":158},"Graph API: allMembers",{"type":39,"tag":80,"props":160,"children":161},{},[162],{"type":39,"tag":131,"props":163,"children":166},{"href":164,"rel":165},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fgraph\u002Fapi\u002Fchannel-get-filesfolder?view=graph-rest-1.0",[135],[167],{"type":44,"value":168},"Graph API: filesFolder",{"type":39,"tag":80,"props":170,"children":171},{},[172],{"type":39,"tag":131,"props":173,"children":176},{"href":174,"rel":175},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fgraph\u002Fteams-changenotifications-channelmembership",[135],[177],{"type":44,"value":178},"Graph API: channel membership change notifications",{"type":39,"tag":80,"props":180,"children":181},{},[182],{"type":39,"tag":131,"props":183,"children":186},{"href":184,"rel":185},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fdotnet\u002Fapi\u002Fmicrosoft.teams.apps?view=msteams-sdk-dotnet-latest",[135],[187],{"type":44,"value":188},"Teams SDK (.NET)",{"type":39,"tag":80,"props":190,"children":191},{},[192],{"type":39,"tag":131,"props":193,"children":196},{"href":194,"rel":195},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fjavascript\u002Fapi\u002Fteams-sdk-typescript\u002F?view=msteams-sdk-ts-latest",[135],[197],{"type":44,"value":198},"Teams SDK (TypeScript)",{"type":39,"tag":55,"props":200,"children":202},{"id":201},"rule-categories",[203],{"type":44,"value":204},"Rule Categories",{"type":39,"tag":206,"props":207,"children":208},"table",{},[209,233],{"type":39,"tag":210,"props":211,"children":212},"thead",{},[213],{"type":39,"tag":214,"props":215,"children":216},"tr",{},[217,223,228],{"type":39,"tag":218,"props":219,"children":220},"th",{},[221],{"type":44,"value":222},"ID",{"type":39,"tag":218,"props":224,"children":225},{},[226],{"type":44,"value":227},"Category",{"type":39,"tag":218,"props":229,"children":230},{},[231],{"type":44,"value":232},"Description",{"type":39,"tag":234,"props":235,"children":236},"tbody",{},[237,260,282,304,326,348,370,392],{"type":39,"tag":214,"props":238,"children":239},{},[240,250,255],{"type":39,"tag":241,"props":242,"children":243},"td",{},[244],{"type":39,"tag":131,"props":245,"children":247},{"href":246},"#man--manifest-rules",[248],{"type":44,"value":249},"MAN",{"type":39,"tag":241,"props":251,"children":252},{},[253],{"type":44,"value":254},"Manifest",{"type":39,"tag":241,"props":256,"children":257},{},[258],{"type":44,"value":259},"App manifest declarations required for NGC",{"type":39,"tag":214,"props":261,"children":262},{},[263,272,277],{"type":39,"tag":241,"props":264,"children":265},{},[266],{"type":39,"tag":131,"props":267,"children":269},{"href":268},"#mem--membership-rules",[270],{"type":44,"value":271},"MEM",{"type":39,"tag":241,"props":273,"children":274},{},[275],{"type":44,"value":276},"Membership",{"type":39,"tag":241,"props":278,"children":279},{},[280],{"type":44,"value":281},"Correct use of channel membership APIs",{"type":39,"tag":214,"props":283,"children":284},{},[285,294,299],{"type":39,"tag":241,"props":286,"children":287},{},[288],{"type":39,"tag":131,"props":289,"children":291},{"href":290},"#fs--file--sharepoint-rules",[292],{"type":44,"value":293},"FS",{"type":39,"tag":241,"props":295,"children":296},{},[297],{"type":44,"value":298},"File & SharePoint",{"type":39,"tag":241,"props":300,"children":301},{},[302],{"type":44,"value":303},"Channel-scoped file and drive access",{"type":39,"tag":214,"props":305,"children":306},{},[307,316,321],{"type":39,"tag":241,"props":308,"children":309},{},[310],{"type":39,"tag":131,"props":311,"children":313},{"href":312},"#bot--bot--messaging-rules",[314],{"type":44,"value":315},"BOT",{"type":39,"tag":241,"props":317,"children":318},{},[319],{"type":44,"value":320},"Bot & Messaging",{"type":39,"tag":241,"props":322,"children":323},{},[324],{"type":44,"value":325},"Bot installation, events, and proactive messaging",{"type":39,"tag":214,"props":327,"children":328},{},[329,338,343],{"type":39,"tag":241,"props":330,"children":331},{},[332],{"type":39,"tag":131,"props":333,"children":335},{"href":334},"#auth--authentication--external-user-rules",[336],{"type":44,"value":337},"AUTH",{"type":39,"tag":241,"props":339,"children":340},{},[341],{"type":44,"value":342},"Auth & External Users",{"type":39,"tag":241,"props":344,"children":345},{},[346],{"type":44,"value":347},"Cross-tenant auth and external user handling",{"type":39,"tag":214,"props":349,"children":350},{},[351,360,365],{"type":39,"tag":241,"props":352,"children":353},{},[354],{"type":39,"tag":131,"props":355,"children":357},{"href":356},"#data--data-scoping-rules",[358],{"type":44,"value":359},"DATA",{"type":39,"tag":241,"props":361,"children":362},{},[363],{"type":44,"value":364},"Data Scoping",{"type":39,"tag":241,"props":366,"children":367},{},[368],{"type":44,"value":369},"Preventing cross-channel data leaks",{"type":39,"tag":214,"props":371,"children":372},{},[373,382,387],{"type":39,"tag":241,"props":374,"children":375},{},[376],{"type":39,"tag":131,"props":377,"children":379},{"href":378},"#sub--graph-subscription-rules",[380],{"type":44,"value":381},"SUB",{"type":39,"tag":241,"props":383,"children":384},{},[385],{"type":44,"value":386},"Graph Subscriptions",{"type":39,"tag":241,"props":388,"children":389},{},[390],{"type":44,"value":391},"Correct subscription configuration for membership changes",{"type":39,"tag":214,"props":393,"children":394},{},[395,404,409],{"type":39,"tag":241,"props":396,"children":397},{},[398],{"type":39,"tag":131,"props":399,"children":401},{"href":400},"#ctx--context-api-rules",[402],{"type":44,"value":403},"CTX",{"type":39,"tag":241,"props":405,"children":406},{},[407],{"type":44,"value":408},"Context API",{"type":39,"tag":241,"props":410,"children":411},{},[412],{"type":44,"value":413},"Using getContext() correctly for NGC",{"type":39,"tag":100,"props":415,"children":416},{},[],{"type":39,"tag":55,"props":418,"children":420},{"id":419},"man-manifest-rules",[421],{"type":44,"value":422},"MAN — Manifest Rules",{"type":39,"tag":424,"props":425,"children":427},"h3",{"id":426},"man-1-declare-supportschannelfeatures",[428,430],{"type":44,"value":429},"MAN-1 · Declare ",{"type":39,"tag":67,"props":431,"children":433},{"className":432},[],[434],{"type":44,"value":435},"supportsChannelFeatures",{"type":39,"tag":40,"props":437,"children":438},{},[439],{"type":39,"tag":47,"props":440,"children":441},{},[442],{"type":44,"value":443},"Severity: CRITICAL",{"type":39,"tag":40,"props":445,"children":446},{},[447,449,454,456,462],{"type":44,"value":448},"Add ",{"type":39,"tag":67,"props":450,"children":452},{"className":451},[],[453],{"type":44,"value":435},{"type":44,"value":455}," with value ",{"type":39,"tag":67,"props":457,"children":459},{"className":458},[],[460],{"type":44,"value":461},"tier1",{"type":44,"value":463}," to your app manifest (v1.25+) to make the app available in shared and private channels.",{"type":39,"tag":465,"props":466,"children":471},"pre",{"className":467,"code":468,"language":469,"meta":470,"style":470},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"manifestVersion\": \"1.25\",\n  \"supportsChannelFeatures\": \"tier1\"\n}\n","json","",[472],{"type":39,"tag":67,"props":473,"children":474},{"__ignoreMap":470},[475,487,531,564],{"type":39,"tag":476,"props":477,"children":480},"span",{"class":478,"line":479},"line",1,[481],{"type":39,"tag":476,"props":482,"children":484},{"style":483},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[485],{"type":44,"value":486},"{\n",{"type":39,"tag":476,"props":488,"children":489},{"class":478,"line":26},[490,495,501,506,511,516,522,526],{"type":39,"tag":476,"props":491,"children":492},{"style":483},[493],{"type":44,"value":494},"  \"",{"type":39,"tag":476,"props":496,"children":498},{"style":497},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[499],{"type":44,"value":500},"manifestVersion",{"type":39,"tag":476,"props":502,"children":503},{"style":483},[504],{"type":44,"value":505},"\"",{"type":39,"tag":476,"props":507,"children":508},{"style":483},[509],{"type":44,"value":510},":",{"type":39,"tag":476,"props":512,"children":513},{"style":483},[514],{"type":44,"value":515}," \"",{"type":39,"tag":476,"props":517,"children":519},{"style":518},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[520],{"type":44,"value":521},"1.25",{"type":39,"tag":476,"props":523,"children":524},{"style":483},[525],{"type":44,"value":505},{"type":39,"tag":476,"props":527,"children":528},{"style":483},[529],{"type":44,"value":530},",\n",{"type":39,"tag":476,"props":532,"children":534},{"class":478,"line":533},3,[535,539,543,547,551,555,559],{"type":39,"tag":476,"props":536,"children":537},{"style":483},[538],{"type":44,"value":494},{"type":39,"tag":476,"props":540,"children":541},{"style":497},[542],{"type":44,"value":435},{"type":39,"tag":476,"props":544,"children":545},{"style":483},[546],{"type":44,"value":505},{"type":39,"tag":476,"props":548,"children":549},{"style":483},[550],{"type":44,"value":510},{"type":39,"tag":476,"props":552,"children":553},{"style":483},[554],{"type":44,"value":515},{"type":39,"tag":476,"props":556,"children":557},{"style":518},[558],{"type":44,"value":461},{"type":39,"tag":476,"props":560,"children":561},{"style":483},[562],{"type":44,"value":563},"\"\n",{"type":39,"tag":476,"props":565,"children":567},{"class":478,"line":566},4,[568],{"type":39,"tag":476,"props":569,"children":570},{"style":483},[571],{"type":44,"value":572},"}\n",{"type":39,"tag":40,"props":574,"children":575},{},[576,581],{"type":39,"tag":47,"props":577,"children":578},{},[579],{"type":44,"value":580},"If missing:",{"type":44,"value":582}," The app will not appear in the channel app picker. Users cannot install it in shared or private channels.",{"type":39,"tag":40,"props":584,"children":585},{},[586],{"type":39,"tag":47,"props":587,"children":588},{},[589],{"type":44,"value":590},"Notes:",{"type":39,"tag":76,"props":592,"children":593},{},[594,604],{"type":39,"tag":80,"props":595,"children":596},{},[597,602],{"type":39,"tag":67,"props":598,"children":600},{"className":599},[],[601],{"type":44,"value":461},{"type":44,"value":603}," has no dependency on classic or admin-level access controls.",{"type":39,"tag":80,"props":605,"children":606},{},[607],{"type":44,"value":608},"Message extension–only apps scoped to personal use do not require this flag.",{"type":39,"tag":100,"props":610,"children":611},{},[],{"type":39,"tag":424,"props":613,"children":615},{"id":614},"man-2-declare-rsc-permissions-when-using-channel-membership-or-file-apis",[616],{"type":44,"value":617},"MAN-2 · Declare RSC permissions when using channel membership or file APIs",{"type":39,"tag":40,"props":619,"children":620},{},[621],{"type":39,"tag":47,"props":622,"children":623},{},[624],{"type":44,"value":625},"Severity: HIGH",{"type":39,"tag":40,"props":627,"children":628},{},[629,631,636],{"type":44,"value":630},"RSC permissions are ",{"type":39,"tag":47,"props":632,"children":633},{},[634],{"type":44,"value":635},"conditional",{"type":44,"value":637}," — only required if the app actually calls the relevant Graph APIs. Do not flag these as violations unless the code scan (Step 3) confirms the app uses the corresponding APIs.",{"type":39,"tag":76,"props":639,"children":640},{},[641,671],{"type":39,"tag":80,"props":642,"children":643},{},[644,653,655,661,663,669],{"type":39,"tag":47,"props":645,"children":646},{},[647],{"type":39,"tag":67,"props":648,"children":650},{"className":649},[],[651],{"type":44,"value":652},"ChannelMember.Read.Group",{"type":44,"value":654}," — required only if the app calls ",{"type":39,"tag":67,"props":656,"children":658},{"className":657},[],[659],{"type":44,"value":660},"allMembers",{"type":44,"value":662},", ",{"type":39,"tag":67,"props":664,"children":666},{"className":665},[],[667],{"type":44,"value":668},"members",{"type":44,"value":670},", or any channel membership Graph API using RSC (not service auth with delegated permissions).",{"type":39,"tag":80,"props":672,"children":673},{},[674,683,684,690],{"type":39,"tag":47,"props":675,"children":676},{},[677],{"type":39,"tag":67,"props":678,"children":680},{"className":679},[],[681],{"type":44,"value":682},"File.Read.Group",{"type":44,"value":654},{"type":39,"tag":67,"props":685,"children":687},{"className":686},[],[688],{"type":44,"value":689},"filesFolder",{"type":44,"value":691}," or accesses channel files via Graph API using RSC.",{"type":39,"tag":465,"props":693,"children":695},{"className":467,"code":694,"language":469,"meta":470,"style":470},"{\n  \"authorization\": {\n    \"permissions\": {\n      \"resourceSpecific\": [\n        {\n          \"name\": \"ChannelMember.Read.Group\",\n          \"type\": \"Application\"\n        },\n        {\n          \"name\": \"File.Read.Group\",\n          \"type\": \"Application\"\n        }\n      ]\n    }\n  }\n}\n",[696],{"type":39,"tag":67,"props":697,"children":698},{"__ignoreMap":470},[699,706,731,757,784,793,832,865,874,882,918,950,959,968,977,986],{"type":39,"tag":476,"props":700,"children":701},{"class":478,"line":479},[702],{"type":39,"tag":476,"props":703,"children":704},{"style":483},[705],{"type":44,"value":486},{"type":39,"tag":476,"props":707,"children":708},{"class":478,"line":26},[709,713,718,722,726],{"type":39,"tag":476,"props":710,"children":711},{"style":483},[712],{"type":44,"value":494},{"type":39,"tag":476,"props":714,"children":715},{"style":497},[716],{"type":44,"value":717},"authorization",{"type":39,"tag":476,"props":719,"children":720},{"style":483},[721],{"type":44,"value":505},{"type":39,"tag":476,"props":723,"children":724},{"style":483},[725],{"type":44,"value":510},{"type":39,"tag":476,"props":727,"children":728},{"style":483},[729],{"type":44,"value":730}," {\n",{"type":39,"tag":476,"props":732,"children":733},{"class":478,"line":533},[734,739,745,749,753],{"type":39,"tag":476,"props":735,"children":736},{"style":483},[737],{"type":44,"value":738},"    \"",{"type":39,"tag":476,"props":740,"children":742},{"style":741},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[743],{"type":44,"value":744},"permissions",{"type":39,"tag":476,"props":746,"children":747},{"style":483},[748],{"type":44,"value":505},{"type":39,"tag":476,"props":750,"children":751},{"style":483},[752],{"type":44,"value":510},{"type":39,"tag":476,"props":754,"children":755},{"style":483},[756],{"type":44,"value":730},{"type":39,"tag":476,"props":758,"children":759},{"class":478,"line":566},[760,765,771,775,779],{"type":39,"tag":476,"props":761,"children":762},{"style":483},[763],{"type":44,"value":764},"      \"",{"type":39,"tag":476,"props":766,"children":768},{"style":767},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[769],{"type":44,"value":770},"resourceSpecific",{"type":39,"tag":476,"props":772,"children":773},{"style":483},[774],{"type":44,"value":505},{"type":39,"tag":476,"props":776,"children":777},{"style":483},[778],{"type":44,"value":510},{"type":39,"tag":476,"props":780,"children":781},{"style":483},[782],{"type":44,"value":783}," [\n",{"type":39,"tag":476,"props":785,"children":787},{"class":478,"line":786},5,[788],{"type":39,"tag":476,"props":789,"children":790},{"style":483},[791],{"type":44,"value":792},"        {\n",{"type":39,"tag":476,"props":794,"children":796},{"class":478,"line":795},6,[797,802,808,812,816,820,824,828],{"type":39,"tag":476,"props":798,"children":799},{"style":483},[800],{"type":44,"value":801},"          \"",{"type":39,"tag":476,"props":803,"children":805},{"style":804},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[806],{"type":44,"value":807},"name",{"type":39,"tag":476,"props":809,"children":810},{"style":483},[811],{"type":44,"value":505},{"type":39,"tag":476,"props":813,"children":814},{"style":483},[815],{"type":44,"value":510},{"type":39,"tag":476,"props":817,"children":818},{"style":483},[819],{"type":44,"value":515},{"type":39,"tag":476,"props":821,"children":822},{"style":518},[823],{"type":44,"value":652},{"type":39,"tag":476,"props":825,"children":826},{"style":483},[827],{"type":44,"value":505},{"type":39,"tag":476,"props":829,"children":830},{"style":483},[831],{"type":44,"value":530},{"type":39,"tag":476,"props":833,"children":834},{"class":478,"line":22},[835,839,844,848,852,856,861],{"type":39,"tag":476,"props":836,"children":837},{"style":483},[838],{"type":44,"value":801},{"type":39,"tag":476,"props":840,"children":841},{"style":804},[842],{"type":44,"value":843},"type",{"type":39,"tag":476,"props":845,"children":846},{"style":483},[847],{"type":44,"value":505},{"type":39,"tag":476,"props":849,"children":850},{"style":483},[851],{"type":44,"value":510},{"type":39,"tag":476,"props":853,"children":854},{"style":483},[855],{"type":44,"value":515},{"type":39,"tag":476,"props":857,"children":858},{"style":518},[859],{"type":44,"value":860},"Application",{"type":39,"tag":476,"props":862,"children":863},{"style":483},[864],{"type":44,"value":563},{"type":39,"tag":476,"props":866,"children":868},{"class":478,"line":867},8,[869],{"type":39,"tag":476,"props":870,"children":871},{"style":483},[872],{"type":44,"value":873},"        },\n",{"type":39,"tag":476,"props":875,"children":877},{"class":478,"line":876},9,[878],{"type":39,"tag":476,"props":879,"children":880},{"style":483},[881],{"type":44,"value":792},{"type":39,"tag":476,"props":883,"children":885},{"class":478,"line":884},10,[886,890,894,898,902,906,910,914],{"type":39,"tag":476,"props":887,"children":888},{"style":483},[889],{"type":44,"value":801},{"type":39,"tag":476,"props":891,"children":892},{"style":804},[893],{"type":44,"value":807},{"type":39,"tag":476,"props":895,"children":896},{"style":483},[897],{"type":44,"value":505},{"type":39,"tag":476,"props":899,"children":900},{"style":483},[901],{"type":44,"value":510},{"type":39,"tag":476,"props":903,"children":904},{"style":483},[905],{"type":44,"value":515},{"type":39,"tag":476,"props":907,"children":908},{"style":518},[909],{"type":44,"value":682},{"type":39,"tag":476,"props":911,"children":912},{"style":483},[913],{"type":44,"value":505},{"type":39,"tag":476,"props":915,"children":916},{"style":483},[917],{"type":44,"value":530},{"type":39,"tag":476,"props":919,"children":921},{"class":478,"line":920},11,[922,926,930,934,938,942,946],{"type":39,"tag":476,"props":923,"children":924},{"style":483},[925],{"type":44,"value":801},{"type":39,"tag":476,"props":927,"children":928},{"style":804},[929],{"type":44,"value":843},{"type":39,"tag":476,"props":931,"children":932},{"style":483},[933],{"type":44,"value":505},{"type":39,"tag":476,"props":935,"children":936},{"style":483},[937],{"type":44,"value":510},{"type":39,"tag":476,"props":939,"children":940},{"style":483},[941],{"type":44,"value":515},{"type":39,"tag":476,"props":943,"children":944},{"style":518},[945],{"type":44,"value":860},{"type":39,"tag":476,"props":947,"children":948},{"style":483},[949],{"type":44,"value":563},{"type":39,"tag":476,"props":951,"children":953},{"class":478,"line":952},12,[954],{"type":39,"tag":476,"props":955,"children":956},{"style":483},[957],{"type":44,"value":958},"        }\n",{"type":39,"tag":476,"props":960,"children":962},{"class":478,"line":961},13,[963],{"type":39,"tag":476,"props":964,"children":965},{"style":483},[966],{"type":44,"value":967},"      ]\n",{"type":39,"tag":476,"props":969,"children":971},{"class":478,"line":970},14,[972],{"type":39,"tag":476,"props":973,"children":974},{"style":483},[975],{"type":44,"value":976},"    }\n",{"type":39,"tag":476,"props":978,"children":980},{"class":478,"line":979},15,[981],{"type":39,"tag":476,"props":982,"children":983},{"style":483},[984],{"type":44,"value":985},"  }\n",{"type":39,"tag":476,"props":987,"children":989},{"class":478,"line":988},16,[990],{"type":39,"tag":476,"props":991,"children":992},{"style":483},[993],{"type":44,"value":572},{"type":39,"tag":40,"props":995,"children":996},{},[997,1002],{"type":39,"tag":47,"props":998,"children":999},{},[1000],{"type":44,"value":1001},"If missing when required:",{"type":44,"value":1003}," Channel membership API calls return 403. File API calls return 403 for RSC-only apps.",{"type":39,"tag":40,"props":1005,"children":1006},{},[1007,1012],{"type":39,"tag":47,"props":1008,"children":1009},{},[1010],{"type":44,"value":1011},"If the app uses service-level auth (client credentials, OBO with delegated Graph permissions) instead of RSC:",{"type":44,"value":1013}," These manifest entries are not required.",{"type":39,"tag":100,"props":1015,"children":1016},{},[],{"type":39,"tag":55,"props":1018,"children":1020},{"id":1019},"mem-membership-rules",[1021],{"type":44,"value":1022},"MEM — Membership Rules",{"type":39,"tag":424,"props":1024,"children":1026},{"id":1025},"mem-1-never-assume-team-membership-channel-membership",[1027],{"type":44,"value":1028},"MEM-1 · Never assume team membership = channel membership",{"type":39,"tag":40,"props":1030,"children":1031},{},[1032],{"type":39,"tag":47,"props":1033,"children":1034},{},[1035],{"type":44,"value":443},{"type":39,"tag":40,"props":1037,"children":1038},{},[1039,1041,1047,1049,1055],{"type":44,"value":1040},"Do not use ",{"type":39,"tag":67,"props":1042,"children":1044},{"className":1043},[],[1045],{"type":44,"value":1046},"\u002Fteams\u002F{team-id}\u002Fmembers",{"type":44,"value":1048}," or AAD group membership (",{"type":39,"tag":67,"props":1050,"children":1052},{"className":1051},[],[1053],{"type":44,"value":1054},"\u002Fgroups\u002F{group-id}\u002Fmembers",{"type":44,"value":1056},") to infer who is in a channel.",{"type":39,"tag":40,"props":1058,"children":1059},{},[1060],{"type":39,"tag":47,"props":1061,"children":1062},{},[1063],{"type":44,"value":1064},"What breaks:",{"type":39,"tag":76,"props":1066,"children":1067},{},[1068,1073,1078,1083],{"type":39,"tag":80,"props":1069,"children":1070},{},[1071],{"type":44,"value":1072},"Tasks assigned to users who are not in the channel.",{"type":39,"tag":80,"props":1074,"children":1075},{},[1076],{"type":44,"value":1077},"User pickers showing people who cannot see the channel.",{"type":39,"tag":80,"props":1079,"children":1080},{},[1081],{"type":44,"value":1082},"Mentions sent to users without channel access.",{"type":39,"tag":80,"props":1084,"children":1085},{},[1086],{"type":44,"value":1087},"External-only channel members are missed entirely.",{"type":39,"tag":40,"props":1089,"children":1090},{},[1091,1096,1098,1103],{"type":39,"tag":47,"props":1092,"children":1093},{},[1094],{"type":44,"value":1095},"Correct approach:",{"type":44,"value":1097}," Always use the channel-scoped ",{"type":39,"tag":67,"props":1099,"children":1101},{"className":1100},[],[1102],{"type":44,"value":660},{"type":44,"value":1104}," API:",{"type":39,"tag":465,"props":1106,"children":1110},{"className":1107,"code":1108,"language":1109,"meta":470,"style":470},"language-http shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","GET \u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FallMembers\n","http",[1111],{"type":39,"tag":67,"props":1112,"children":1113},{"__ignoreMap":470},[1114],{"type":39,"tag":476,"props":1115,"children":1116},{"class":478,"line":479},[1117],{"type":39,"tag":476,"props":1118,"children":1119},{},[1120],{"type":44,"value":1108},{"type":39,"tag":100,"props":1122,"children":1123},{},[],{"type":39,"tag":424,"props":1125,"children":1127},{"id":1126},"mem-2-use-allmembers-api-not-members-for-channel-roster",[1128,1130,1135,1137,1142],{"type":44,"value":1129},"MEM-2 · Use ",{"type":39,"tag":67,"props":1131,"children":1133},{"className":1132},[],[1134],{"type":44,"value":660},{"type":44,"value":1136}," API — not ",{"type":39,"tag":67,"props":1138,"children":1140},{"className":1139},[],[1141],{"type":44,"value":668},{"type":44,"value":1143}," — for channel roster",{"type":39,"tag":40,"props":1145,"children":1146},{},[1147],{"type":39,"tag":47,"props":1148,"children":1149},{},[1150],{"type":44,"value":443},{"type":39,"tag":40,"props":1152,"children":1153},{},[1154,1160,1162,1167,1169,1174],{"type":39,"tag":67,"props":1155,"children":1157},{"className":1156},[],[1158],{"type":44,"value":1159},"\u002Fchannels\u002F{id}\u002Fmembers",{"type":44,"value":1161}," returns ",{"type":39,"tag":47,"props":1163,"children":1164},{},[1165],{"type":44,"value":1166},"only directly added members",{"type":44,"value":1168},". Use ",{"type":39,"tag":67,"props":1170,"children":1172},{"className":1171},[],[1173],{"type":44,"value":660},{"type":44,"value":1175}," to include both direct and indirect members (users who joined via a shared team).",{"type":39,"tag":465,"props":1177,"children":1178},{"className":1107,"code":1108,"language":1109,"meta":470,"style":470},[1179],{"type":39,"tag":67,"props":1180,"children":1181},{"__ignoreMap":470},[1182],{"type":39,"tag":476,"props":1183,"children":1184},{"class":478,"line":479},[1185],{"type":39,"tag":476,"props":1186,"children":1187},{},[1188],{"type":44,"value":1108},{"type":39,"tag":40,"props":1190,"children":1191},{},[1192],{"type":39,"tag":47,"props":1193,"children":1194},{},[1195],{"type":44,"value":1196},"Direct vs. Indirect members:",{"type":39,"tag":76,"props":1198,"children":1199},{},[1200,1210],{"type":39,"tag":80,"props":1201,"children":1202},{},[1203,1208],{"type":39,"tag":47,"props":1204,"children":1205},{},[1206],{"type":44,"value":1207},"Direct:",{"type":44,"value":1209}," Explicitly added to the channel (including cross-tenant users).",{"type":39,"tag":80,"props":1211,"children":1212},{},[1213,1218,1220,1226],{"type":39,"tag":47,"props":1214,"children":1215},{},[1216],{"type":44,"value":1217},"Indirect:",{"type":44,"value":1219}," Members of a team that the channel is shared with. These are identified by the ",{"type":39,"tag":67,"props":1221,"children":1223},{"className":1222},[],[1224],{"type":44,"value":1225},"@microsoft.graph.originalSourceMembershipUrl",{"type":44,"value":1227}," annotation pointing to their source team.",{"type":39,"tag":40,"props":1229,"children":1230},{},[1231],{"type":39,"tag":47,"props":1232,"children":1233},{},[1234,1236,1242,1244,1250],{"type":44,"value":1235},"SDK equivalents — scan for VIOLATIONS (",{"type":39,"tag":67,"props":1237,"children":1239},{"className":1238},[],[1240],{"type":44,"value":1241},".Members",{"type":44,"value":1243}," instead of ",{"type":39,"tag":67,"props":1245,"children":1247},{"className":1246},[],[1248],{"type":44,"value":1249},".AllMembers",{"type":44,"value":1251},"):",{"type":39,"tag":206,"props":1253,"children":1254},{},[1255,1276],{"type":39,"tag":210,"props":1256,"children":1257},{},[1258],{"type":39,"tag":214,"props":1259,"children":1260},{},[1261,1266,1271],{"type":39,"tag":218,"props":1262,"children":1263},{},[1264],{"type":44,"value":1265},"Language",{"type":39,"tag":218,"props":1267,"children":1268},{},[1269],{"type":44,"value":1270},"Violation (direct only)",{"type":39,"tag":218,"props":1272,"children":1273},{},[1274],{"type":44,"value":1275},"Correct (all members)",{"type":39,"tag":234,"props":1277,"children":1278},{},[1279,1305,1331,1357,1383,1409],{"type":39,"tag":214,"props":1280,"children":1281},{},[1282,1287,1296],{"type":39,"tag":241,"props":1283,"children":1284},{},[1285],{"type":44,"value":1286},"C# (Graph SDK)",{"type":39,"tag":241,"props":1288,"children":1289},{},[1290],{"type":39,"tag":67,"props":1291,"children":1293},{"className":1292},[],[1294],{"type":44,"value":1295},"graphClient.Teams[id].Channels[id].Members.GetAsync()",{"type":39,"tag":241,"props":1297,"children":1298},{},[1299],{"type":39,"tag":67,"props":1300,"children":1302},{"className":1301},[],[1303],{"type":44,"value":1304},"graphClient.Teams[id].Channels[id].AllMembers.GetAsync()",{"type":39,"tag":214,"props":1306,"children":1307},{},[1308,1313,1322],{"type":39,"tag":241,"props":1309,"children":1310},{},[1311],{"type":44,"value":1312},"JavaScript",{"type":39,"tag":241,"props":1314,"children":1315},{},[1316],{"type":39,"tag":67,"props":1317,"children":1319},{"className":1318},[],[1320],{"type":44,"value":1321},"client.api('\u002Fteams\u002F...\u002Fchannels\u002F...\u002Fmembers').get()",{"type":39,"tag":241,"props":1323,"children":1324},{},[1325],{"type":39,"tag":67,"props":1326,"children":1328},{"className":1327},[],[1329],{"type":44,"value":1330},"client.api('\u002Fteams\u002F...\u002Fchannels\u002F...\u002FallMembers').get()",{"type":39,"tag":214,"props":1332,"children":1333},{},[1334,1339,1348],{"type":39,"tag":241,"props":1335,"children":1336},{},[1337],{"type":44,"value":1338},"Java",{"type":39,"tag":241,"props":1340,"children":1341},{},[1342],{"type":39,"tag":67,"props":1343,"children":1345},{"className":1344},[],[1346],{"type":44,"value":1347},"graphClient.teams().byTeamId(id).channels().byChannelId(id).members().get()",{"type":39,"tag":241,"props":1349,"children":1350},{},[1351],{"type":39,"tag":67,"props":1352,"children":1354},{"className":1353},[],[1355],{"type":44,"value":1356},"...allMembers().get()",{"type":39,"tag":214,"props":1358,"children":1359},{},[1360,1365,1374],{"type":39,"tag":241,"props":1361,"children":1362},{},[1363],{"type":44,"value":1364},"Python",{"type":39,"tag":241,"props":1366,"children":1367},{},[1368],{"type":39,"tag":67,"props":1369,"children":1371},{"className":1370},[],[1372],{"type":44,"value":1373},"graph_client.teams.by_team_id(id).channels.by_channel_id(id).members.get()",{"type":39,"tag":241,"props":1375,"children":1376},{},[1377],{"type":39,"tag":67,"props":1378,"children":1380},{"className":1379},[],[1381],{"type":44,"value":1382},"...all_members.get()",{"type":39,"tag":214,"props":1384,"children":1385},{},[1386,1391,1400],{"type":39,"tag":241,"props":1387,"children":1388},{},[1389],{"type":44,"value":1390},"Teams SDK (.NET new)",{"type":39,"tag":241,"props":1392,"children":1393},{},[1394],{"type":39,"tag":67,"props":1395,"children":1397},{"className":1396},[],[1398],{"type":44,"value":1399},"context.Api.Teams[id].Channels[id].Members.GetAsync()",{"type":39,"tag":241,"props":1401,"children":1402},{},[1403],{"type":39,"tag":67,"props":1404,"children":1406},{"className":1405},[],[1407],{"type":44,"value":1408},"...AllMembers.GetAsync()",{"type":39,"tag":214,"props":1410,"children":1411},{},[1412,1417,1426],{"type":39,"tag":241,"props":1413,"children":1414},{},[1415],{"type":44,"value":1416},"Teams SDK (TS new)",{"type":39,"tag":241,"props":1418,"children":1419},{},[1420],{"type":39,"tag":67,"props":1421,"children":1423},{"className":1422},[],[1424],{"type":44,"value":1425},"app.api.teams(id).channels(id).members.get()",{"type":39,"tag":241,"props":1427,"children":1428},{},[1429],{"type":39,"tag":67,"props":1430,"children":1432},{"className":1431},[],[1433],{"type":44,"value":1434},"...allMembers.get()",{"type":39,"tag":100,"props":1436,"children":1437},{},[],{"type":39,"tag":424,"props":1439,"children":1441},{"id":1440},"mem-3-check-doesuserhaveaccess-before-acting-on-member-removed-events",[1442,1444,1450],{"type":44,"value":1443},"MEM-3 · Check ",{"type":39,"tag":67,"props":1445,"children":1447},{"className":1446},[],[1448],{"type":44,"value":1449},"doesUserHaveAccess",{"type":44,"value":1451}," before acting on member-removed events",{"type":39,"tag":40,"props":1453,"children":1454},{},[1455],{"type":39,"tag":47,"props":1456,"children":1457},{},[1458],{"type":44,"value":625},{"type":39,"tag":40,"props":1460,"children":1461},{},[1462,1464,1470],{"type":44,"value":1463},"When a ",{"type":39,"tag":67,"props":1465,"children":1467},{"className":1466},[],[1468],{"type":44,"value":1469},"member removed",{"type":44,"value":1471}," notification arrives for an indirect member, the user may still have access via a direct membership path. Always verify before revoking:",{"type":39,"tag":465,"props":1473,"children":1475},{"className":1107,"code":1474,"language":1109,"meta":470,"style":470},"GET \u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FdoesUserHaveAccess(userId='@userid',tenantId='@TenantID',userPrincipalName='@UserPrincipalName')\n",[1476],{"type":39,"tag":67,"props":1477,"children":1478},{"__ignoreMap":470},[1479],{"type":39,"tag":476,"props":1480,"children":1481},{"class":478,"line":479},[1482],{"type":39,"tag":476,"props":1483,"children":1484},{},[1485],{"type":44,"value":1474},{"type":39,"tag":40,"props":1487,"children":1488},{},[1489,1494],{"type":39,"tag":47,"props":1490,"children":1491},{},[1492],{"type":44,"value":1493},"If skipped:",{"type":44,"value":1495}," Users are incorrectly treated as removed when they still have channel access via a different path.",{"type":39,"tag":100,"props":1497,"children":1498},{},[],{"type":39,"tag":424,"props":1500,"children":1502},{"id":1501},"mem-4-do-not-assume-everyone-in-the-team-can-see-every-channel",[1503],{"type":44,"value":1504},"MEM-4 · Do not assume \"everyone in the team\" can see every channel",{"type":39,"tag":40,"props":1506,"children":1507},{},[1508],{"type":39,"tag":47,"props":1509,"children":1510},{},[1511],{"type":44,"value":625},{"type":39,"tag":40,"props":1513,"children":1514},{},[1515,1517,1522],{"type":44,"value":1516},"Private channels are restricted subsets of the team. Shared channels may include members from entirely different teams or tenants. Team-wide \"add all\" or \"notify all\" flows must not use team membership as a proxy for channel visibility. Always rely on ",{"type":39,"tag":67,"props":1518,"children":1520},{"className":1519},[],[1521],{"type":44,"value":660},{"type":44,"value":1523}," API.",{"type":39,"tag":465,"props":1525,"children":1526},{"className":1107,"code":1108,"language":1109,"meta":470,"style":470},[1527],{"type":39,"tag":67,"props":1528,"children":1529},{"__ignoreMap":470},[1530],{"type":39,"tag":476,"props":1531,"children":1532},{"class":478,"line":479},[1533],{"type":39,"tag":476,"props":1534,"children":1535},{},[1536],{"type":44,"value":1108},{"type":39,"tag":100,"props":1538,"children":1539},{},[],{"type":39,"tag":424,"props":1541,"children":1543},{"id":1542},"mem-5-use-hostteamgroupid-not-groupid-as-team-id-in-membership-api-calls",[1544,1546,1552,1554,1560,1562,1568],{"type":44,"value":1545},"MEM-5 · Use ",{"type":39,"tag":67,"props":1547,"children":1549},{"className":1548},[],[1550],{"type":44,"value":1551},"hostTeamGroupID",{"type":44,"value":1553}," (not ",{"type":39,"tag":67,"props":1555,"children":1557},{"className":1556},[],[1558],{"type":44,"value":1559},"groupId",{"type":44,"value":1561},") as ",{"type":39,"tag":67,"props":1563,"children":1565},{"className":1564},[],[1566],{"type":44,"value":1567},"team-id",{"type":44,"value":1569}," in membership API calls",{"type":39,"tag":40,"props":1571,"children":1572},{},[1573],{"type":39,"tag":47,"props":1574,"children":1575},{},[1576],{"type":44,"value":443},{"type":39,"tag":40,"props":1578,"children":1579},{},[1580,1582,1588],{"type":44,"value":1581},"In a shared or private channel, ",{"type":39,"tag":67,"props":1583,"children":1585},{"className":1584},[],[1586],{"type":44,"value":1587},"getContext()",{"type":44,"value":1589}," returns two channel-specific properties:",{"type":39,"tag":76,"props":1591,"children":1592},{},[1593,1610],{"type":39,"tag":80,"props":1594,"children":1595},{},[1596,1601,1603,1608],{"type":39,"tag":67,"props":1597,"children":1599},{"className":1598},[],[1600],{"type":44,"value":1551},{"type":44,"value":1602}," — the AAD group ID of the ",{"type":39,"tag":47,"props":1604,"children":1605},{},[1606],{"type":44,"value":1607},"host team",{"type":44,"value":1609}," that owns the channel",{"type":39,"tag":80,"props":1611,"children":1612},{},[1613,1619],{"type":39,"tag":67,"props":1614,"children":1616},{"className":1615},[],[1617],{"type":44,"value":1618},"hostTenantID",{"type":44,"value":1620}," — the tenant ID of the host team",{"type":39,"tag":40,"props":1622,"children":1623},{},[1624,1626,1631,1633,1638,1640,1645,1647,1652,1654,1659],{"type":44,"value":1625},"The plain ",{"type":39,"tag":67,"props":1627,"children":1629},{"className":1628},[],[1630],{"type":44,"value":1559},{"type":44,"value":1632}," from ",{"type":39,"tag":67,"props":1634,"children":1636},{"className":1635},[],[1637],{"type":44,"value":1587},{"type":44,"value":1639}," is the group ID of the team the user is currently navigated under, which in a shared channel may be a ",{"type":39,"tag":47,"props":1641,"children":1642},{},[1643],{"type":44,"value":1644},"different team",{"type":44,"value":1646}," than the one hosting the channel. Using ",{"type":39,"tag":67,"props":1648,"children":1650},{"className":1649},[],[1651],{"type":44,"value":1559},{"type":44,"value":1653}," as the ",{"type":39,"tag":67,"props":1655,"children":1657},{"className":1656},[],[1658],{"type":44,"value":1567},{"type":44,"value":1660}," in Graph calls targets the wrong team and returns incorrect or empty membership.",{"type":39,"tag":40,"props":1662,"children":1663},{},[1664],{"type":39,"tag":47,"props":1665,"children":1666},{},[1667],{"type":44,"value":1668},"Incorrect (shared\u002Fprivate channel context):",{"type":39,"tag":465,"props":1670,"children":1674},{"className":1671,"code":1672,"language":1673,"meta":470,"style":470},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const context = await microsoftTeams.app.getContext();\nconst teamId = context.team.groupId; \u002F\u002F ❌ may be the wrong team in shared channels\n","js",[1675],{"type":39,"tag":67,"props":1676,"children":1677},{"__ignoreMap":470},[1678,1738],{"type":39,"tag":476,"props":1679,"children":1680},{"class":478,"line":479},[1681,1686,1692,1697,1703,1708,1713,1718,1722,1728,1733],{"type":39,"tag":476,"props":1682,"children":1683},{"style":497},[1684],{"type":44,"value":1685},"const",{"type":39,"tag":476,"props":1687,"children":1689},{"style":1688},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1690],{"type":44,"value":1691}," context ",{"type":39,"tag":476,"props":1693,"children":1694},{"style":483},[1695],{"type":44,"value":1696},"=",{"type":39,"tag":476,"props":1698,"children":1700},{"style":1699},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[1701],{"type":44,"value":1702}," await",{"type":39,"tag":476,"props":1704,"children":1705},{"style":1688},[1706],{"type":44,"value":1707}," microsoftTeams",{"type":39,"tag":476,"props":1709,"children":1710},{"style":483},[1711],{"type":44,"value":1712},".",{"type":39,"tag":476,"props":1714,"children":1715},{"style":1688},[1716],{"type":44,"value":1717},"app",{"type":39,"tag":476,"props":1719,"children":1720},{"style":483},[1721],{"type":44,"value":1712},{"type":39,"tag":476,"props":1723,"children":1725},{"style":1724},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[1726],{"type":44,"value":1727},"getContext",{"type":39,"tag":476,"props":1729,"children":1730},{"style":1688},[1731],{"type":44,"value":1732},"()",{"type":39,"tag":476,"props":1734,"children":1735},{"style":483},[1736],{"type":44,"value":1737},";\n",{"type":39,"tag":476,"props":1739,"children":1740},{"class":478,"line":26},[1741,1745,1750,1754,1759,1763,1768,1772,1776,1781],{"type":39,"tag":476,"props":1742,"children":1743},{"style":497},[1744],{"type":44,"value":1685},{"type":39,"tag":476,"props":1746,"children":1747},{"style":1688},[1748],{"type":44,"value":1749}," teamId ",{"type":39,"tag":476,"props":1751,"children":1752},{"style":483},[1753],{"type":44,"value":1696},{"type":39,"tag":476,"props":1755,"children":1756},{"style":1688},[1757],{"type":44,"value":1758}," context",{"type":39,"tag":476,"props":1760,"children":1761},{"style":483},[1762],{"type":44,"value":1712},{"type":39,"tag":476,"props":1764,"children":1765},{"style":1688},[1766],{"type":44,"value":1767},"team",{"type":39,"tag":476,"props":1769,"children":1770},{"style":483},[1771],{"type":44,"value":1712},{"type":39,"tag":476,"props":1773,"children":1774},{"style":1688},[1775],{"type":44,"value":1559},{"type":39,"tag":476,"props":1777,"children":1778},{"style":483},[1779],{"type":44,"value":1780},";",{"type":39,"tag":476,"props":1782,"children":1784},{"style":1783},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[1785],{"type":44,"value":1786}," \u002F\u002F ❌ may be the wrong team in shared channels\n",{"type":39,"tag":40,"props":1788,"children":1789},{},[1790],{"type":39,"tag":47,"props":1791,"children":1792},{},[1793],{"type":44,"value":1794},"Correct:",{"type":39,"tag":465,"props":1796,"children":1798},{"className":1671,"code":1797,"language":1673,"meta":470,"style":470},"const context = await microsoftTeams.app.getContext();\nconst teamId = context.hostTeamGroupID; \u002F\u002F ✅ always the channel's host team\nconst channelId = context.channel.id;\n\u002F\u002F GET \u002Fteams\u002F{teamId}\u002Fchannels\u002F{channelId}\u002FallMembers\n",[1799],{"type":39,"tag":67,"props":1800,"children":1801},{"__ignoreMap":470},[1802,1849,1885,1927],{"type":39,"tag":476,"props":1803,"children":1804},{"class":478,"line":479},[1805,1809,1813,1817,1821,1825,1829,1833,1837,1841,1845],{"type":39,"tag":476,"props":1806,"children":1807},{"style":497},[1808],{"type":44,"value":1685},{"type":39,"tag":476,"props":1810,"children":1811},{"style":1688},[1812],{"type":44,"value":1691},{"type":39,"tag":476,"props":1814,"children":1815},{"style":483},[1816],{"type":44,"value":1696},{"type":39,"tag":476,"props":1818,"children":1819},{"style":1699},[1820],{"type":44,"value":1702},{"type":39,"tag":476,"props":1822,"children":1823},{"style":1688},[1824],{"type":44,"value":1707},{"type":39,"tag":476,"props":1826,"children":1827},{"style":483},[1828],{"type":44,"value":1712},{"type":39,"tag":476,"props":1830,"children":1831},{"style":1688},[1832],{"type":44,"value":1717},{"type":39,"tag":476,"props":1834,"children":1835},{"style":483},[1836],{"type":44,"value":1712},{"type":39,"tag":476,"props":1838,"children":1839},{"style":1724},[1840],{"type":44,"value":1727},{"type":39,"tag":476,"props":1842,"children":1843},{"style":1688},[1844],{"type":44,"value":1732},{"type":39,"tag":476,"props":1846,"children":1847},{"style":483},[1848],{"type":44,"value":1737},{"type":39,"tag":476,"props":1850,"children":1851},{"class":478,"line":26},[1852,1856,1860,1864,1868,1872,1876,1880],{"type":39,"tag":476,"props":1853,"children":1854},{"style":497},[1855],{"type":44,"value":1685},{"type":39,"tag":476,"props":1857,"children":1858},{"style":1688},[1859],{"type":44,"value":1749},{"type":39,"tag":476,"props":1861,"children":1862},{"style":483},[1863],{"type":44,"value":1696},{"type":39,"tag":476,"props":1865,"children":1866},{"style":1688},[1867],{"type":44,"value":1758},{"type":39,"tag":476,"props":1869,"children":1870},{"style":483},[1871],{"type":44,"value":1712},{"type":39,"tag":476,"props":1873,"children":1874},{"style":1688},[1875],{"type":44,"value":1551},{"type":39,"tag":476,"props":1877,"children":1878},{"style":483},[1879],{"type":44,"value":1780},{"type":39,"tag":476,"props":1881,"children":1882},{"style":1783},[1883],{"type":44,"value":1884}," \u002F\u002F ✅ always the channel's host team\n",{"type":39,"tag":476,"props":1886,"children":1887},{"class":478,"line":533},[1888,1892,1897,1901,1905,1909,1914,1918,1923],{"type":39,"tag":476,"props":1889,"children":1890},{"style":497},[1891],{"type":44,"value":1685},{"type":39,"tag":476,"props":1893,"children":1894},{"style":1688},[1895],{"type":44,"value":1896}," channelId ",{"type":39,"tag":476,"props":1898,"children":1899},{"style":483},[1900],{"type":44,"value":1696},{"type":39,"tag":476,"props":1902,"children":1903},{"style":1688},[1904],{"type":44,"value":1758},{"type":39,"tag":476,"props":1906,"children":1907},{"style":483},[1908],{"type":44,"value":1712},{"type":39,"tag":476,"props":1910,"children":1911},{"style":1688},[1912],{"type":44,"value":1913},"channel",{"type":39,"tag":476,"props":1915,"children":1916},{"style":483},[1917],{"type":44,"value":1712},{"type":39,"tag":476,"props":1919,"children":1920},{"style":1688},[1921],{"type":44,"value":1922},"id",{"type":39,"tag":476,"props":1924,"children":1925},{"style":483},[1926],{"type":44,"value":1737},{"type":39,"tag":476,"props":1928,"children":1929},{"class":478,"line":566},[1930],{"type":39,"tag":476,"props":1931,"children":1932},{"style":1783},[1933],{"type":44,"value":1934},"\u002F\u002F GET \u002Fteams\u002F{teamId}\u002Fchannels\u002F{channelId}\u002FallMembers\n",{"type":39,"tag":40,"props":1936,"children":1937},{},[1938,1943,1945,1951,1953,1959],{"type":39,"tag":47,"props":1939,"children":1940},{},[1941],{"type":44,"value":1942},"For bots:",{"type":44,"value":1944}," Use ",{"type":39,"tag":67,"props":1946,"children":1948},{"className":1947},[],[1949],{"type":44,"value":1950},"turnContext.Activity.GetChannelData\u003CTeamsChannelData>()?.Team?.Id",{"type":44,"value":1952}," or ",{"type":39,"tag":67,"props":1954,"children":1956},{"className":1955},[],[1957],{"type":44,"value":1958},"turnContext.Activity.TeamsGetTeamInfo()?.AadGroupId",{"type":44,"value":1960}," as the team-id.",{"type":39,"tag":40,"props":1962,"children":1963},{},[1964],{"type":39,"tag":47,"props":1965,"children":1966},{},[1967,1969,1974],{"type":44,"value":1968},"If ",{"type":39,"tag":67,"props":1970,"children":1972},{"className":1971},[],[1973],{"type":44,"value":1559},{"type":44,"value":1975}," is used instead:",{"type":39,"tag":76,"props":1977,"children":1978},{},[1979,1984,1989],{"type":39,"tag":80,"props":1980,"children":1981},{},[1982],{"type":44,"value":1983},"Membership API returns members of the wrong team.",{"type":39,"tag":80,"props":1985,"children":1986},{},[1987],{"type":44,"value":1988},"Cross-tenant shared channel members are missed entirely.",{"type":39,"tag":80,"props":1990,"children":1991},{},[1992],{"type":44,"value":1993},"Channel API calls may return 404 because the channel belongs to a different host team.",{"type":39,"tag":40,"props":1995,"children":1996},{},[1997,1999,2004,2006,2011,2013,2018],{"type":44,"value":1998},"Note that ",{"type":39,"tag":67,"props":2000,"children":2002},{"className":2001},[],[2003],{"type":44,"value":1551},{"type":44,"value":2005}," and ",{"type":39,"tag":67,"props":2007,"children":2009},{"className":2008},[],[2010],{"type":44,"value":1618},{"type":44,"value":2012}," are not applicable for all URLs or URL templates. They are applicable only for Graph API calls, as values read from ",{"type":39,"tag":67,"props":2014,"children":2016},{"className":2015},[],[2017],{"type":44,"value":1587},{"type":44,"value":2019}," to be passed in the URL. There are no query parameters or placeholders that app developers need to use with these names.",{"type":39,"tag":100,"props":2021,"children":2022},{},[],{"type":39,"tag":424,"props":2024,"children":2026},{"id":2025},"mem-6-inspect-microsoftgraphoriginalsourcemembershipurl-to-classify-direct-vs-indirect-members",[2027,2029,2034],{"type":44,"value":2028},"MEM-6 · Inspect ",{"type":39,"tag":67,"props":2030,"children":2032},{"className":2031},[],[2033],{"type":44,"value":1225},{"type":44,"value":2035}," to classify direct vs indirect members",{"type":39,"tag":40,"props":2037,"children":2038},{},[2039],{"type":39,"tag":47,"props":2040,"children":2041},{},[2042],{"type":44,"value":2043},"Severity: MEDIUM",{"type":39,"tag":40,"props":2045,"children":2046},{},[2047,2048,2053,2055,2060],{"type":44,"value":84},{"type":39,"tag":67,"props":2049,"children":2051},{"className":2050},[],[2052],{"type":44,"value":660},{"type":44,"value":2054}," API and indirect membership change notifications include the ",{"type":39,"tag":67,"props":2056,"children":2058},{"className":2057},[],[2059],{"type":44,"value":1225},{"type":44,"value":2061}," annotation on each member object. Apps must read this annotation to correctly classify members:",{"type":39,"tag":76,"props":2063,"children":2064},{},[2065,2075],{"type":39,"tag":80,"props":2066,"children":2067},{},[2068,2073],{"type":39,"tag":47,"props":2069,"children":2070},{},[2071],{"type":44,"value":2072},"Direct member:",{"type":44,"value":2074}," The annotation URL points back to this channel's own membership endpoint.",{"type":39,"tag":80,"props":2076,"children":2077},{},[2078,2083],{"type":39,"tag":47,"props":2079,"children":2080},{},[2081],{"type":44,"value":2082},"Indirect member:",{"type":44,"value":2084}," The annotation URL points to a different team (the source team that shares the channel).",{"type":39,"tag":465,"props":2086,"children":2088},{"className":467,"code":2087,"language":469,"meta":470,"style":470},"{\n  \"@microsoft.graph.originalSourceMembershipUrl\": \"https:\u002F\u002Fgraph.microsoft.com\u002Fv1.0\u002Ftenants\u002F{tenant}\u002Fteams\u002F{sourceTeamId}\u002Fchannels\u002F{channelId}\u002Fmembers\u002F{memberId}\",\n  \"id\": \"...\",\n  \"roles\": [\"owner\"],\n  \"displayName\": \"Jane Doe\",\n  \"tenantId\": \"...\"\n}\n",[2089],{"type":39,"tag":67,"props":2090,"children":2091},{"__ignoreMap":470},[2092,2099,2135,2171,2214,2251,2283],{"type":39,"tag":476,"props":2093,"children":2094},{"class":478,"line":479},[2095],{"type":39,"tag":476,"props":2096,"children":2097},{"style":483},[2098],{"type":44,"value":486},{"type":39,"tag":476,"props":2100,"children":2101},{"class":478,"line":26},[2102,2106,2110,2114,2118,2122,2127,2131],{"type":39,"tag":476,"props":2103,"children":2104},{"style":483},[2105],{"type":44,"value":494},{"type":39,"tag":476,"props":2107,"children":2108},{"style":497},[2109],{"type":44,"value":1225},{"type":39,"tag":476,"props":2111,"children":2112},{"style":483},[2113],{"type":44,"value":505},{"type":39,"tag":476,"props":2115,"children":2116},{"style":483},[2117],{"type":44,"value":510},{"type":39,"tag":476,"props":2119,"children":2120},{"style":483},[2121],{"type":44,"value":515},{"type":39,"tag":476,"props":2123,"children":2124},{"style":518},[2125],{"type":44,"value":2126},"https:\u002F\u002Fgraph.microsoft.com\u002Fv1.0\u002Ftenants\u002F{tenant}\u002Fteams\u002F{sourceTeamId}\u002Fchannels\u002F{channelId}\u002Fmembers\u002F{memberId}",{"type":39,"tag":476,"props":2128,"children":2129},{"style":483},[2130],{"type":44,"value":505},{"type":39,"tag":476,"props":2132,"children":2133},{"style":483},[2134],{"type":44,"value":530},{"type":39,"tag":476,"props":2136,"children":2137},{"class":478,"line":533},[2138,2142,2146,2150,2154,2158,2163,2167],{"type":39,"tag":476,"props":2139,"children":2140},{"style":483},[2141],{"type":44,"value":494},{"type":39,"tag":476,"props":2143,"children":2144},{"style":497},[2145],{"type":44,"value":1922},{"type":39,"tag":476,"props":2147,"children":2148},{"style":483},[2149],{"type":44,"value":505},{"type":39,"tag":476,"props":2151,"children":2152},{"style":483},[2153],{"type":44,"value":510},{"type":39,"tag":476,"props":2155,"children":2156},{"style":483},[2157],{"type":44,"value":515},{"type":39,"tag":476,"props":2159,"children":2160},{"style":518},[2161],{"type":44,"value":2162},"...",{"type":39,"tag":476,"props":2164,"children":2165},{"style":483},[2166],{"type":44,"value":505},{"type":39,"tag":476,"props":2168,"children":2169},{"style":483},[2170],{"type":44,"value":530},{"type":39,"tag":476,"props":2172,"children":2173},{"class":478,"line":566},[2174,2178,2183,2187,2191,2196,2200,2205,2209],{"type":39,"tag":476,"props":2175,"children":2176},{"style":483},[2177],{"type":44,"value":494},{"type":39,"tag":476,"props":2179,"children":2180},{"style":497},[2181],{"type":44,"value":2182},"roles",{"type":39,"tag":476,"props":2184,"children":2185},{"style":483},[2186],{"type":44,"value":505},{"type":39,"tag":476,"props":2188,"children":2189},{"style":483},[2190],{"type":44,"value":510},{"type":39,"tag":476,"props":2192,"children":2193},{"style":483},[2194],{"type":44,"value":2195}," [",{"type":39,"tag":476,"props":2197,"children":2198},{"style":483},[2199],{"type":44,"value":505},{"type":39,"tag":476,"props":2201,"children":2202},{"style":518},[2203],{"type":44,"value":2204},"owner",{"type":39,"tag":476,"props":2206,"children":2207},{"style":483},[2208],{"type":44,"value":505},{"type":39,"tag":476,"props":2210,"children":2211},{"style":483},[2212],{"type":44,"value":2213},"],\n",{"type":39,"tag":476,"props":2215,"children":2216},{"class":478,"line":786},[2217,2221,2226,2230,2234,2238,2243,2247],{"type":39,"tag":476,"props":2218,"children":2219},{"style":483},[2220],{"type":44,"value":494},{"type":39,"tag":476,"props":2222,"children":2223},{"style":497},[2224],{"type":44,"value":2225},"displayName",{"type":39,"tag":476,"props":2227,"children":2228},{"style":483},[2229],{"type":44,"value":505},{"type":39,"tag":476,"props":2231,"children":2232},{"style":483},[2233],{"type":44,"value":510},{"type":39,"tag":476,"props":2235,"children":2236},{"style":483},[2237],{"type":44,"value":515},{"type":39,"tag":476,"props":2239,"children":2240},{"style":518},[2241],{"type":44,"value":2242},"Jane Doe",{"type":39,"tag":476,"props":2244,"children":2245},{"style":483},[2246],{"type":44,"value":505},{"type":39,"tag":476,"props":2248,"children":2249},{"style":483},[2250],{"type":44,"value":530},{"type":39,"tag":476,"props":2252,"children":2253},{"class":478,"line":795},[2254,2258,2263,2267,2271,2275,2279],{"type":39,"tag":476,"props":2255,"children":2256},{"style":483},[2257],{"type":44,"value":494},{"type":39,"tag":476,"props":2259,"children":2260},{"style":497},[2261],{"type":44,"value":2262},"tenantId",{"type":39,"tag":476,"props":2264,"children":2265},{"style":483},[2266],{"type":44,"value":505},{"type":39,"tag":476,"props":2268,"children":2269},{"style":483},[2270],{"type":44,"value":510},{"type":39,"tag":476,"props":2272,"children":2273},{"style":483},[2274],{"type":44,"value":515},{"type":39,"tag":476,"props":2276,"children":2277},{"style":518},[2278],{"type":44,"value":2162},{"type":39,"tag":476,"props":2280,"children":2281},{"style":483},[2282],{"type":44,"value":563},{"type":39,"tag":476,"props":2284,"children":2285},{"class":478,"line":22},[2286],{"type":39,"tag":476,"props":2287,"children":2288},{"style":483},[2289],{"type":44,"value":572},{"type":39,"tag":40,"props":2291,"children":2292},{},[2293],{"type":39,"tag":47,"props":2294,"children":2295},{},[2296],{"type":44,"value":2297},"If not handled:",{"type":39,"tag":76,"props":2299,"children":2300},{},[2301,2306],{"type":39,"tag":80,"props":2302,"children":2303},{},[2304],{"type":44,"value":2305},"Apps that remove all \"removed\" users without checking direct\u002Findirect paths may incorrectly revoke access.",{"type":39,"tag":80,"props":2307,"children":2308},{},[2309],{"type":44,"value":2310},"Deduplication of double-notifications for users who are both direct and indirect members requires this annotation.",{"type":39,"tag":100,"props":2312,"children":2313},{},[],{"type":39,"tag":55,"props":2315,"children":2317},{"id":2316},"fs-file-sharepoint-rules",[2318],{"type":44,"value":2319},"FS — File & SharePoint Rules",{"type":39,"tag":424,"props":2321,"children":2323},{"id":2322},"fs-1-never-assume-one-sharepoint-site-per-team",[2324],{"type":44,"value":2325},"FS-1 · Never assume one SharePoint site per team",{"type":39,"tag":40,"props":2327,"children":2328},{},[2329],{"type":39,"tag":47,"props":2330,"children":2331},{},[2332],{"type":44,"value":443},{"type":39,"tag":40,"props":2334,"children":2335},{},[2336,2338,2343,2345,2351],{"type":44,"value":2337},"Standard channels share the team's SharePoint site. Shared and private channels each have their ",{"type":39,"tag":47,"props":2339,"children":2340},{},[2341],{"type":44,"value":2342},"own",{"type":44,"value":2344}," dedicated SharePoint site and ",{"type":39,"tag":67,"props":2346,"children":2348},{"className":2347},[],[2349],{"type":44,"value":2350},"driveId",{"type":44,"value":1712},{"type":39,"tag":40,"props":2353,"children":2354},{},[2355],{"type":39,"tag":47,"props":2356,"children":2357},{},[2358],{"type":44,"value":2359},"If assumed:",{"type":39,"tag":76,"props":2361,"children":2362},{},[2363,2368,2373],{"type":39,"tag":80,"props":2364,"children":2365},{},[2366],{"type":44,"value":2367},"Files are written to the wrong site.",{"type":39,"tag":80,"props":2369,"children":2370},{},[2371],{"type":44,"value":2372},"File reads return 404 or 403.",{"type":39,"tag":80,"props":2374,"children":2375},{},[2376],{"type":44,"value":2377},"Content leaks across channel boundaries.",{"type":39,"tag":100,"props":2379,"children":2380},{},[],{"type":39,"tag":424,"props":2382,"children":2384},{"id":2383},"fs-2-always-resolve-driveid-and-itemid-per-channel-via-filesfolder",[2385,2387,2392,2393,2399,2401],{"type":44,"value":2386},"FS-2 · Always resolve ",{"type":39,"tag":67,"props":2388,"children":2390},{"className":2389},[],[2391],{"type":44,"value":2350},{"type":44,"value":2005},{"type":39,"tag":67,"props":2394,"children":2396},{"className":2395},[],[2397],{"type":44,"value":2398},"itemId",{"type":44,"value":2400}," per channel via ",{"type":39,"tag":67,"props":2402,"children":2404},{"className":2403},[],[2405],{"type":44,"value":689},{"type":39,"tag":40,"props":2407,"children":2408},{},[2409],{"type":39,"tag":47,"props":2410,"children":2411},{},[2412],{"type":44,"value":443},{"type":39,"tag":40,"props":2414,"children":2415},{},[2416],{"type":44,"value":2417},"Use this API to get the correct drive root for any channel type:",{"type":39,"tag":465,"props":2419,"children":2421},{"className":1107,"code":2420,"language":1109,"meta":470,"style":470},"GET \u002Fteams\u002F{teamId}\u002Fchannels\u002F{channelId}\u002FfilesFolder\n",[2422],{"type":39,"tag":67,"props":2423,"children":2424},{"__ignoreMap":470},[2425],{"type":39,"tag":476,"props":2426,"children":2427},{"class":478,"line":479},[2428],{"type":39,"tag":476,"props":2429,"children":2430},{},[2431],{"type":44,"value":2420},{"type":39,"tag":40,"props":2433,"children":2434},{},[2435],{"type":44,"value":2436},"Response properties to store and reuse:",{"type":39,"tag":76,"props":2438,"children":2439},{},[2440,2451],{"type":39,"tag":80,"props":2441,"children":2442},{},[2443,2449],{"type":39,"tag":67,"props":2444,"children":2446},{"className":2445},[],[2447],{"type":44,"value":2448},"parentReference.driveId",{"type":44,"value":2450}," — the channel's SharePoint driveId",{"type":39,"tag":80,"props":2452,"children":2453},{},[2454,2459],{"type":39,"tag":67,"props":2455,"children":2457},{"className":2456},[],[2458],{"type":44,"value":1922},{"type":44,"value":2460}," — the folderId for the channel's root",{"type":39,"tag":40,"props":2462,"children":2463},{},[2464,2469,2471,2477],{"type":39,"tag":47,"props":2465,"children":2466},{},[2467],{"type":44,"value":2468},"Never",{"type":44,"value":2470}," hardcode SharePoint library names or paths (e.g., ",{"type":39,"tag":67,"props":2472,"children":2474},{"className":2473},[],[2475],{"type":44,"value":2476},"\u002FShared Documents\u002F{ChannelName}",{"type":44,"value":2478},"). Use this API for all channel types, including standard channels.",{"type":39,"tag":40,"props":2480,"children":2481},{},[2482],{"type":39,"tag":47,"props":2483,"children":2484},{},[2485],{"type":44,"value":2486},"SDK equivalents:",{"type":39,"tag":206,"props":2488,"children":2489},{},[2490,2505],{"type":39,"tag":210,"props":2491,"children":2492},{},[2493],{"type":39,"tag":214,"props":2494,"children":2495},{},[2496,2500],{"type":39,"tag":218,"props":2497,"children":2498},{},[2499],{"type":44,"value":1265},{"type":39,"tag":218,"props":2501,"children":2502},{},[2503],{"type":44,"value":2504},"Correct usage",{"type":39,"tag":234,"props":2506,"children":2507},{},[2508,2524,2540,2556],{"type":39,"tag":214,"props":2509,"children":2510},{},[2511,2515],{"type":39,"tag":241,"props":2512,"children":2513},{},[2514],{"type":44,"value":1286},{"type":39,"tag":241,"props":2516,"children":2517},{},[2518],{"type":39,"tag":67,"props":2519,"children":2521},{"className":2520},[],[2522],{"type":44,"value":2523},"graphClient.Teams[teamId].Channels[channelId].FilesFolder.GetAsync()",{"type":39,"tag":214,"props":2525,"children":2526},{},[2527,2531],{"type":39,"tag":241,"props":2528,"children":2529},{},[2530],{"type":44,"value":1312},{"type":39,"tag":241,"props":2532,"children":2533},{},[2534],{"type":39,"tag":67,"props":2535,"children":2537},{"className":2536},[],[2538],{"type":44,"value":2539},"client.api('\u002Fteams\u002F{id}\u002Fchannels\u002F{id}\u002FfilesFolder').get()",{"type":39,"tag":214,"props":2541,"children":2542},{},[2543,2547],{"type":39,"tag":241,"props":2544,"children":2545},{},[2546],{"type":44,"value":1364},{"type":39,"tag":241,"props":2548,"children":2549},{},[2550],{"type":39,"tag":67,"props":2551,"children":2553},{"className":2552},[],[2554],{"type":44,"value":2555},"graph_client.teams.by_team_id(id).channels.by_channel_id(id).files_folder.get()",{"type":39,"tag":214,"props":2557,"children":2558},{},[2559,2563],{"type":39,"tag":241,"props":2560,"children":2561},{},[2562],{"type":44,"value":1338},{"type":39,"tag":241,"props":2564,"children":2565},{},[2566],{"type":39,"tag":67,"props":2567,"children":2569},{"className":2568},[],[2570],{"type":44,"value":2571},"graphClient.teams().byTeamId(id).channels().byChannelId(id).filesFolder().get()",{"type":39,"tag":111,"props":2573,"children":2574},{},[2575],{"type":39,"tag":40,"props":2576,"children":2577},{},[2578,2583,2585,2590,2592,2598],{"type":39,"tag":47,"props":2579,"children":2580},{},[2581],{"type":44,"value":2582},"Private channel note:",{"type":44,"value":2584}," After the private channel SharePoint migration, ",{"type":39,"tag":67,"props":2586,"children":2588},{"className":2587},[],[2589],{"type":44,"value":689},{"type":44,"value":2591}," returns the root folder's ",{"type":39,"tag":67,"props":2593,"children":2595},{"className":2594},[],[2596],{"type":44,"value":2597},"driveItem",{"type":44,"value":2599},", not a named subfolder. Newly created private channels no longer create a dedicated subfolder — all files go to the root. Do not rely on a named subfolder matching the channel name.",{"type":39,"tag":100,"props":2601,"children":2602},{},[],{"type":39,"tag":424,"props":2604,"children":2606},{"id":2605},"fs-3-do-not-cache-a-single-team-level-driveid-for-all-channels",[2607,2609,2614],{"type":44,"value":2608},"FS-3 · Do not cache a single team-level ",{"type":39,"tag":67,"props":2610,"children":2612},{"className":2611},[],[2613],{"type":44,"value":2350},{"type":44,"value":2615}," for all channels",{"type":39,"tag":40,"props":2617,"children":2618},{},[2619],{"type":39,"tag":47,"props":2620,"children":2621},{},[2622],{"type":44,"value":443},{"type":39,"tag":40,"props":2624,"children":2625},{},[2626,2628,2633],{"type":44,"value":2627},"Each shared\u002Fprivate channel has a distinct ",{"type":39,"tag":67,"props":2629,"children":2631},{"className":2630},[],[2632],{"type":44,"value":2350},{"type":44,"value":2634},". A cached team-level driveId will cause all file operations in shared\u002Fprivate channels to target the wrong drive.",{"type":39,"tag":40,"props":2636,"children":2637},{},[2638,2643,2645,2650,2652,2657],{"type":39,"tag":47,"props":2639,"children":2640},{},[2641],{"type":44,"value":2642},"Correct pattern:",{"type":44,"value":2644}," Cache driveId ",{"type":39,"tag":47,"props":2646,"children":2647},{},[2648],{"type":44,"value":2649},"per channelId",{"type":44,"value":2651},", and refresh via ",{"type":39,"tag":67,"props":2653,"children":2655},{"className":2654},[],[2656],{"type":44,"value":689},{"type":44,"value":2658}," when a new channel is encountered.",{"type":39,"tag":40,"props":2660,"children":2661},{},[2662,2664,2670],{"type":44,"value":2663},"See ",{"type":39,"tag":67,"props":2665,"children":2667},{"className":2666},[],[2668],{"type":44,"value":2669},"FS-2",{"type":44,"value":2671}," section for calling patterns.",{"type":39,"tag":100,"props":2673,"children":2674},{},[],{"type":39,"tag":424,"props":2676,"children":2678},{"id":2677},"fs-4-use-membership-based-sharing-links-not-org-wide-links",[2679],{"type":44,"value":2680},"FS-4 · Use membership-based sharing links, not org-wide links",{"type":39,"tag":40,"props":2682,"children":2683},{},[2684],{"type":39,"tag":47,"props":2685,"children":2686},{},[2687],{"type":44,"value":625},{"type":39,"tag":40,"props":2689,"children":2690},{},[2691],{"type":44,"value":2692},"Organization-wide or anonymous sharing links exclude external members even if they are channel members.",{"type":39,"tag":40,"props":2694,"children":2695},{},[2696],{"type":39,"tag":47,"props":2697,"children":2698},{},[2699],{"type":44,"value":2700},"Do not use:",{"type":39,"tag":76,"props":2702,"children":2703},{},[2704,2709],{"type":39,"tag":80,"props":2705,"children":2706},{},[2707],{"type":44,"value":2708},"Organization-wide links",{"type":39,"tag":80,"props":2710,"children":2711},{},[2712],{"type":44,"value":2713},"Anonymous links for shared channel content",{"type":39,"tag":40,"props":2715,"children":2716},{},[2717],{"type":39,"tag":47,"props":2718,"children":2719},{},[2720],{"type":44,"value":2721},"Use instead:",{"type":39,"tag":76,"props":2723,"children":2724},{},[2725,2738,2743],{"type":39,"tag":80,"props":2726,"children":2727},{},[2728,2730,2736],{"type":44,"value":2729},"Specific-people sharing (",{"type":39,"tag":67,"props":2731,"children":2733},{"className":2732},[],[2734],{"type":44,"value":2735},"\u002Finvite",{"type":44,"value":2737}," API)",{"type":39,"tag":80,"props":2739,"children":2740},{},[2741],{"type":44,"value":2742},"\"People with existing access\" links",{"type":39,"tag":80,"props":2744,"children":2745},{},[2746],{"type":44,"value":2747},"Membership-based permissions",{"type":39,"tag":465,"props":2749,"children":2751},{"className":1107,"code":2750,"language":1109,"meta":470,"style":470},"POST \u002Fdrives\u002F{driveId}\u002Fitems\u002F{itemId}\u002Finvite\n",[2752],{"type":39,"tag":67,"props":2753,"children":2754},{"__ignoreMap":470},[2755],{"type":39,"tag":476,"props":2756,"children":2757},{"class":478,"line":479},[2758],{"type":39,"tag":476,"props":2759,"children":2760},{},[2761],{"type":44,"value":2750},{"type":39,"tag":100,"props":2763,"children":2764},{},[],{"type":39,"tag":424,"props":2766,"children":2768},{"id":2767},"fs-5-declare-filereadgroup-rsc-permission-for-rsc-apps-accessing-channel-files",[2769,2771,2776],{"type":44,"value":2770},"FS-5 · Declare ",{"type":39,"tag":67,"props":2772,"children":2774},{"className":2773},[],[2775],{"type":44,"value":682},{"type":44,"value":2777}," RSC permission for RSC apps accessing channel files",{"type":39,"tag":40,"props":2779,"children":2780},{},[2781,2785,2787],{"type":39,"tag":47,"props":2782,"children":2783},{},[2784],{"type":44,"value":625},{"type":44,"value":2786}," ",{"type":39,"tag":2788,"props":2789,"children":2790},"em",{},[2791],{"type":44,"value":2792},"(conditional — only applies if the app uses RSC auth for file APIs)",{"type":39,"tag":40,"props":2794,"children":2795},{},[2796,2798,2803,2805,2810],{"type":44,"value":2797},"For apps using Resource-Specific Consent (RSC) that access channel files via the Graph API, declare the ",{"type":39,"tag":67,"props":2799,"children":2801},{"className":2800},[],[2802],{"type":44,"value":682},{"type":44,"value":2804}," permission. This is the least-privileged RSC application permission for the ",{"type":39,"tag":67,"props":2806,"children":2808},{"className":2807},[],[2809],{"type":44,"value":689},{"type":44,"value":1523},{"type":39,"tag":40,"props":2812,"children":2813},{},[2814,2819],{"type":39,"tag":47,"props":2815,"children":2816},{},[2817],{"type":44,"value":2818},"This permission is NOT required",{"type":44,"value":2820}," if the app uses service-level auth (client credentials or OBO with delegated Graph permissions) instead of RSC.",{"type":39,"tag":465,"props":2822,"children":2824},{"className":467,"code":2823,"language":469,"meta":470,"style":470},"{\n  \"authorization\": {\n    \"permissions\": {\n      \"resourceSpecific\": [\n        {\n          \"name\": \"File.Read.Group\",\n          \"type\": \"Application\"\n        }\n      ]\n    }\n  }\n}\n",[2825],{"type":39,"tag":67,"props":2826,"children":2827},{"__ignoreMap":470},[2828,2835,2858,2881,2904,2911,2946,2977,2984,2991,2998,3005],{"type":39,"tag":476,"props":2829,"children":2830},{"class":478,"line":479},[2831],{"type":39,"tag":476,"props":2832,"children":2833},{"style":483},[2834],{"type":44,"value":486},{"type":39,"tag":476,"props":2836,"children":2837},{"class":478,"line":26},[2838,2842,2846,2850,2854],{"type":39,"tag":476,"props":2839,"children":2840},{"style":483},[2841],{"type":44,"value":494},{"type":39,"tag":476,"props":2843,"children":2844},{"style":497},[2845],{"type":44,"value":717},{"type":39,"tag":476,"props":2847,"children":2848},{"style":483},[2849],{"type":44,"value":505},{"type":39,"tag":476,"props":2851,"children":2852},{"style":483},[2853],{"type":44,"value":510},{"type":39,"tag":476,"props":2855,"children":2856},{"style":483},[2857],{"type":44,"value":730},{"type":39,"tag":476,"props":2859,"children":2860},{"class":478,"line":533},[2861,2865,2869,2873,2877],{"type":39,"tag":476,"props":2862,"children":2863},{"style":483},[2864],{"type":44,"value":738},{"type":39,"tag":476,"props":2866,"children":2867},{"style":741},[2868],{"type":44,"value":744},{"type":39,"tag":476,"props":2870,"children":2871},{"style":483},[2872],{"type":44,"value":505},{"type":39,"tag":476,"props":2874,"children":2875},{"style":483},[2876],{"type":44,"value":510},{"type":39,"tag":476,"props":2878,"children":2879},{"style":483},[2880],{"type":44,"value":730},{"type":39,"tag":476,"props":2882,"children":2883},{"class":478,"line":566},[2884,2888,2892,2896,2900],{"type":39,"tag":476,"props":2885,"children":2886},{"style":483},[2887],{"type":44,"value":764},{"type":39,"tag":476,"props":2889,"children":2890},{"style":767},[2891],{"type":44,"value":770},{"type":39,"tag":476,"props":2893,"children":2894},{"style":483},[2895],{"type":44,"value":505},{"type":39,"tag":476,"props":2897,"children":2898},{"style":483},[2899],{"type":44,"value":510},{"type":39,"tag":476,"props":2901,"children":2902},{"style":483},[2903],{"type":44,"value":783},{"type":39,"tag":476,"props":2905,"children":2906},{"class":478,"line":786},[2907],{"type":39,"tag":476,"props":2908,"children":2909},{"style":483},[2910],{"type":44,"value":792},{"type":39,"tag":476,"props":2912,"children":2913},{"class":478,"line":795},[2914,2918,2922,2926,2930,2934,2938,2942],{"type":39,"tag":476,"props":2915,"children":2916},{"style":483},[2917],{"type":44,"value":801},{"type":39,"tag":476,"props":2919,"children":2920},{"style":804},[2921],{"type":44,"value":807},{"type":39,"tag":476,"props":2923,"children":2924},{"style":483},[2925],{"type":44,"value":505},{"type":39,"tag":476,"props":2927,"children":2928},{"style":483},[2929],{"type":44,"value":510},{"type":39,"tag":476,"props":2931,"children":2932},{"style":483},[2933],{"type":44,"value":515},{"type":39,"tag":476,"props":2935,"children":2936},{"style":518},[2937],{"type":44,"value":682},{"type":39,"tag":476,"props":2939,"children":2940},{"style":483},[2941],{"type":44,"value":505},{"type":39,"tag":476,"props":2943,"children":2944},{"style":483},[2945],{"type":44,"value":530},{"type":39,"tag":476,"props":2947,"children":2948},{"class":478,"line":22},[2949,2953,2957,2961,2965,2969,2973],{"type":39,"tag":476,"props":2950,"children":2951},{"style":483},[2952],{"type":44,"value":801},{"type":39,"tag":476,"props":2954,"children":2955},{"style":804},[2956],{"type":44,"value":843},{"type":39,"tag":476,"props":2958,"children":2959},{"style":483},[2960],{"type":44,"value":505},{"type":39,"tag":476,"props":2962,"children":2963},{"style":483},[2964],{"type":44,"value":510},{"type":39,"tag":476,"props":2966,"children":2967},{"style":483},[2968],{"type":44,"value":515},{"type":39,"tag":476,"props":2970,"children":2971},{"style":518},[2972],{"type":44,"value":860},{"type":39,"tag":476,"props":2974,"children":2975},{"style":483},[2976],{"type":44,"value":563},{"type":39,"tag":476,"props":2978,"children":2979},{"class":478,"line":867},[2980],{"type":39,"tag":476,"props":2981,"children":2982},{"style":483},[2983],{"type":44,"value":958},{"type":39,"tag":476,"props":2985,"children":2986},{"class":478,"line":876},[2987],{"type":39,"tag":476,"props":2988,"children":2989},{"style":483},[2990],{"type":44,"value":967},{"type":39,"tag":476,"props":2992,"children":2993},{"class":478,"line":884},[2994],{"type":39,"tag":476,"props":2995,"children":2996},{"style":483},[2997],{"type":44,"value":976},{"type":39,"tag":476,"props":2999,"children":3000},{"class":478,"line":920},[3001],{"type":39,"tag":476,"props":3002,"children":3003},{"style":483},[3004],{"type":44,"value":985},{"type":39,"tag":476,"props":3006,"children":3007},{"class":478,"line":952},[3008],{"type":39,"tag":476,"props":3009,"children":3010},{"style":483},[3011],{"type":44,"value":572},{"type":39,"tag":40,"props":3013,"children":3014},{},[3015,3020,3021,3027],{"type":39,"tag":47,"props":3016,"children":3017},{},[3018],{"type":44,"value":3019},"If missing (RSC apps only):",{"type":44,"value":2786},{"type":39,"tag":67,"props":3022,"children":3024},{"className":3023},[],[3025],{"type":44,"value":3026},"GET \u002Fteams\u002F{id}\u002Fchannels\u002F{id}\u002FfilesFolder",{"type":44,"value":3028}," returns 403.",{"type":39,"tag":100,"props":3030,"children":3031},{},[],{"type":39,"tag":55,"props":3033,"children":3035},{"id":3034},"bot-bot-messaging-rules",[3036],{"type":44,"value":3037},"BOT — Bot & Messaging Rules",{"type":39,"tag":424,"props":3039,"children":3041},{"id":3040},"bot-1-do-not-assume-bot-is-present-in-a-channel-just-because-its-in-the-team",[3042],{"type":44,"value":3043},"BOT-1 · Do not assume bot is present in a channel just because it's in the team",{"type":39,"tag":40,"props":3045,"children":3046},{},[3047],{"type":39,"tag":47,"props":3048,"children":3049},{},[3050],{"type":44,"value":443},{"type":39,"tag":40,"props":3052,"children":3053},{},[3054,3056,3061],{"type":44,"value":3055},"Apps installed at the team level are ",{"type":39,"tag":47,"props":3057,"children":3058},{},[3059],{"type":44,"value":3060},"not",{"type":44,"value":3062}," automatically available in shared or private channels. The app must be explicitly added to each channel.",{"type":39,"tag":40,"props":3064,"children":3065},{},[3066],{"type":39,"tag":47,"props":3067,"children":3068},{},[3069],{"type":44,"value":2359},{"type":39,"tag":76,"props":3071,"children":3072},{},[3073,3078],{"type":39,"tag":80,"props":3074,"children":3075},{},[3076],{"type":44,"value":3077},"Proactive messages to the channel fail with 403.",{"type":39,"tag":80,"props":3079,"children":3080},{},[3081],{"type":44,"value":3082},"Bot silently drops messages; users see nothing.",{"type":39,"tag":40,"props":3084,"children":3085},{},[3086,3090,3092,3098],{"type":39,"tag":47,"props":3087,"children":3088},{},[3089],{"type":44,"value":1095},{"type":44,"value":3091}," Use the ",{"type":39,"tag":67,"props":3093,"children":3095},{"className":3094},[],[3096],{"type":44,"value":3097},"channelMemberAdded",{"type":44,"value":3099}," event to detect when the bot is added to a channel and trigger per-channel setup logic.",{"type":39,"tag":100,"props":3101,"children":3102},{},[],{"type":39,"tag":424,"props":3104,"children":3106},{"id":3105},"bot-2-listen-to-channelmemberadded-for-per-channel-bot-setup",[3107,3109,3114],{"type":44,"value":3108},"BOT-2 · Listen to ",{"type":39,"tag":67,"props":3110,"children":3112},{"className":3111},[],[3113],{"type":44,"value":3097},{"type":44,"value":3115}," for per-channel bot setup",{"type":39,"tag":40,"props":3117,"children":3118},{},[3119],{"type":39,"tag":47,"props":3120,"children":3121},{},[3122],{"type":44,"value":625},{"type":39,"tag":40,"props":3124,"children":3125},{},[3126,3128,3133,3135,3141],{"type":44,"value":3127},"When the bot receives a ",{"type":39,"tag":67,"props":3129,"children":3131},{"className":3130},[],[3132],{"type":44,"value":3097},{"type":44,"value":3134}," event inside ",{"type":39,"tag":67,"props":3136,"children":3138},{"className":3137},[],[3139],{"type":44,"value":3140},"OnConversationUpdateActivityAsync",{"type":44,"value":3142},", use it to:",{"type":39,"tag":76,"props":3144,"children":3145},{},[3146,3151,3156,3161],{"type":39,"tag":80,"props":3147,"children":3148},{},[3149],{"type":44,"value":3150},"Send a welcome\u002Fsetup message",{"type":39,"tag":80,"props":3152,"children":3153},{},[3154],{"type":44,"value":3155},"Fetch the channel roster",{"type":39,"tag":80,"props":3157,"children":3158},{},[3159],{"type":44,"value":3160},"Configure tabs",{"type":39,"tag":80,"props":3162,"children":3163},{},[3164],{"type":44,"value":3165},"Start scheduled jobs",{"type":39,"tag":40,"props":3167,"children":3168},{},[3169,3171,3177],{"type":44,"value":3170},"Do not rely solely on team-level install events (",{"type":39,"tag":67,"props":3172,"children":3174},{"className":3173},[],[3175],{"type":44,"value":3176},"teamMemberAdded",{"type":44,"value":3178}," \u002F uninstall) for per-channel logic.",{"type":39,"tag":100,"props":3180,"children":3181},{},[],{"type":39,"tag":424,"props":3183,"children":3185},{"id":3184},"bot-3-handle-channelshared-and-channelunshared-events",[3186,3188,3194,3195,3201],{"type":44,"value":3187},"BOT-3 · Handle ",{"type":39,"tag":67,"props":3189,"children":3191},{"className":3190},[],[3192],{"type":44,"value":3193},"channelshared",{"type":44,"value":2005},{"type":39,"tag":67,"props":3196,"children":3198},{"className":3197},[],[3199],{"type":44,"value":3200},"channelunshared",{"type":44,"value":3202}," events",{"type":39,"tag":40,"props":3204,"children":3205},{},[3206],{"type":39,"tag":47,"props":3207,"children":3208},{},[3209],{"type":44,"value":625},{"type":39,"tag":40,"props":3211,"children":3212},{},[3213,3215,3221,3223,3229,3231,3236,3237,3242],{"type":44,"value":3214},"When a shared channel is added to or removed from another team, your bot receives a ",{"type":39,"tag":67,"props":3216,"children":3218},{"className":3217},[],[3219],{"type":44,"value":3220},"conversationUpdate",{"type":44,"value":3222}," activity with ",{"type":39,"tag":67,"props":3224,"children":3226},{"className":3225},[],[3227],{"type":44,"value":3228},"eventType",{"type":44,"value":3230}," = ",{"type":39,"tag":67,"props":3232,"children":3234},{"className":3233},[],[3235],{"type":44,"value":3193},{"type":44,"value":1952},{"type":39,"tag":67,"props":3238,"children":3240},{"className":3239},[],[3241],{"type":44,"value":3200},{"type":44,"value":1712},{"type":39,"tag":40,"props":3244,"children":3245},{},[3246],{"type":44,"value":3247},"Use these events to:",{"type":39,"tag":76,"props":3249,"children":3250},{},[3251,3256,3261],{"type":39,"tag":80,"props":3252,"children":3253},{},[3254],{"type":44,"value":3255},"Update which channels the bot is active in.",{"type":39,"tag":80,"props":3257,"children":3258},{},[3259],{"type":44,"value":3260},"Stop sending proactive messages to channels the bot is no longer associated with.",{"type":39,"tag":80,"props":3262,"children":3263},{},[3264,3266,3271],{"type":44,"value":3265},"Trigger a membership refresh via ",{"type":39,"tag":67,"props":3267,"children":3269},{"className":3268},[],[3270],{"type":44,"value":660},{"type":44,"value":1712},{"type":39,"tag":465,"props":3273,"children":3277},{"className":3274,"code":3275,"language":3276,"meta":470,"style":470},"language-csharp shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","var tcd = turnContext.Activity.GetChannelData\u003CTeamsChannelData>();\nvar eventType = tcd?.EventType?.ToLowerInvariant();\n\nswitch (eventType)\n{\n    case \"channelshared\":\n        \u002F\u002F Refresh membership, update state\n        break;\n    case \"channelunshared\":\n        \u002F\u002F Remove channel from active list\n        break;\n}\n","csharp",[3278],{"type":39,"tag":67,"props":3279,"children":3280},{"__ignoreMap":470},[3281,3289,3297,3306,3314,3321,3329,3337,3345,3353,3361,3368],{"type":39,"tag":476,"props":3282,"children":3283},{"class":478,"line":479},[3284],{"type":39,"tag":476,"props":3285,"children":3286},{},[3287],{"type":44,"value":3288},"var tcd = turnContext.Activity.GetChannelData\u003CTeamsChannelData>();\n",{"type":39,"tag":476,"props":3290,"children":3291},{"class":478,"line":26},[3292],{"type":39,"tag":476,"props":3293,"children":3294},{},[3295],{"type":44,"value":3296},"var eventType = tcd?.EventType?.ToLowerInvariant();\n",{"type":39,"tag":476,"props":3298,"children":3299},{"class":478,"line":533},[3300],{"type":39,"tag":476,"props":3301,"children":3303},{"emptyLinePlaceholder":3302},true,[3304],{"type":44,"value":3305},"\n",{"type":39,"tag":476,"props":3307,"children":3308},{"class":478,"line":566},[3309],{"type":39,"tag":476,"props":3310,"children":3311},{},[3312],{"type":44,"value":3313},"switch (eventType)\n",{"type":39,"tag":476,"props":3315,"children":3316},{"class":478,"line":786},[3317],{"type":39,"tag":476,"props":3318,"children":3319},{},[3320],{"type":44,"value":486},{"type":39,"tag":476,"props":3322,"children":3323},{"class":478,"line":795},[3324],{"type":39,"tag":476,"props":3325,"children":3326},{},[3327],{"type":44,"value":3328},"    case \"channelshared\":\n",{"type":39,"tag":476,"props":3330,"children":3331},{"class":478,"line":22},[3332],{"type":39,"tag":476,"props":3333,"children":3334},{},[3335],{"type":44,"value":3336},"        \u002F\u002F Refresh membership, update state\n",{"type":39,"tag":476,"props":3338,"children":3339},{"class":478,"line":867},[3340],{"type":39,"tag":476,"props":3341,"children":3342},{},[3343],{"type":44,"value":3344},"        break;\n",{"type":39,"tag":476,"props":3346,"children":3347},{"class":478,"line":876},[3348],{"type":39,"tag":476,"props":3349,"children":3350},{},[3351],{"type":44,"value":3352},"    case \"channelunshared\":\n",{"type":39,"tag":476,"props":3354,"children":3355},{"class":478,"line":884},[3356],{"type":39,"tag":476,"props":3357,"children":3358},{},[3359],{"type":44,"value":3360},"        \u002F\u002F Remove channel from active list\n",{"type":39,"tag":476,"props":3362,"children":3363},{"class":478,"line":920},[3364],{"type":39,"tag":476,"props":3365,"children":3366},{},[3367],{"type":44,"value":3344},{"type":39,"tag":476,"props":3369,"children":3370},{"class":478,"line":952},[3371],{"type":39,"tag":476,"props":3372,"children":3373},{},[3374],{"type":44,"value":572},{"type":39,"tag":40,"props":3376,"children":3377},{},[3378,3382],{"type":39,"tag":47,"props":3379,"children":3380},{},[3381],{"type":44,"value":2297},{"type":44,"value":3383}," Bot keeps sending to channels it's no longer part of, generating repeated 403 errors and stale state.",{"type":39,"tag":100,"props":3385,"children":3386},{},[],{"type":39,"tag":424,"props":3388,"children":3390},{"id":3389},"bot-4-send-welcome-messages-on-channelmemberadded-not-only-on-team-install",[3391,3393,3398],{"type":44,"value":3392},"BOT-4 · Send welcome messages on ",{"type":39,"tag":67,"props":3394,"children":3396},{"className":3395},[],[3397],{"type":44,"value":3097},{"type":44,"value":3399},", not only on team install",{"type":39,"tag":40,"props":3401,"children":3402},{},[3403],{"type":39,"tag":47,"props":3404,"children":3405},{},[3406],{"type":44,"value":2043},{"type":39,"tag":40,"props":3408,"children":3409},{},[3410],{"type":44,"value":3411},"If welcome\u002Fsetup logic only fires on team install events, shared and private channels never receive it when the bot is first enabled there.",{"type":39,"tag":100,"props":3413,"children":3414},{},[],{"type":39,"tag":424,"props":3416,"children":3418},{"id":3417},"bot-5-use-getchanneldatasharedchannelchanneldata-for-shared-channel-event-payloads",[3419,3421,3427],{"type":44,"value":3420},"BOT-5 · Use ",{"type":39,"tag":67,"props":3422,"children":3424},{"className":3423},[],[3425],{"type":44,"value":3426},"GetChannelData\u003CSharedChannelChannelData>()",{"type":44,"value":3428}," for shared channel event payloads",{"type":39,"tag":40,"props":3430,"children":3431},{},[3432],{"type":39,"tag":47,"props":3433,"children":3434},{},[3435],{"type":44,"value":625},{"type":39,"tag":40,"props":3437,"children":3438},{},[3439,3441,3446,3447,3452,3454,3460,3462,3468],{"type":44,"value":3440},"For ",{"type":39,"tag":67,"props":3442,"children":3444},{"className":3443},[],[3445],{"type":44,"value":3193},{"type":44,"value":2005},{"type":39,"tag":67,"props":3448,"children":3450},{"className":3449},[],[3451],{"type":44,"value":3200},{"type":44,"value":3453}," events, the full payload (including the list of teams the channel was shared with or unshared from) is only available via ",{"type":39,"tag":67,"props":3455,"children":3457},{"className":3456},[],[3458],{"type":44,"value":3459},"SharedChannelChannelData",{"type":44,"value":3461},", not the base ",{"type":39,"tag":67,"props":3463,"children":3465},{"className":3464},[],[3466],{"type":44,"value":3467},"TeamsChannelData",{"type":44,"value":1712},{"type":39,"tag":465,"props":3470,"children":3472},{"className":3274,"code":3471,"language":3276,"meta":470,"style":470},"\u002F\u002F ❌ Insufficient — misses SharedWithTeams \u002F UnsharedFromTeams lists\nvar tcd = turnContext.Activity.GetChannelData\u003CTeamsChannelData>();\n\n\u002F\u002F ✅ Correct — gives access to SharedWithTeams and UnsharedFromTeams\nvar extended = turnContext.Activity.GetChannelData\u003CSharedChannelChannelData>();\nvar sharedWith = extended?.SharedWithTeams ?? new List\u003CTeamInfoEx>();\nvar unsharedFrom = extended?.UnsharedFromTeams ?? new List\u003CTeamInfoEx>();\n",[3473],{"type":39,"tag":67,"props":3474,"children":3475},{"__ignoreMap":470},[3476,3484,3491,3498,3506,3514,3522],{"type":39,"tag":476,"props":3477,"children":3478},{"class":478,"line":479},[3479],{"type":39,"tag":476,"props":3480,"children":3481},{},[3482],{"type":44,"value":3483},"\u002F\u002F ❌ Insufficient — misses SharedWithTeams \u002F UnsharedFromTeams lists\n",{"type":39,"tag":476,"props":3485,"children":3486},{"class":478,"line":26},[3487],{"type":39,"tag":476,"props":3488,"children":3489},{},[3490],{"type":44,"value":3288},{"type":39,"tag":476,"props":3492,"children":3493},{"class":478,"line":533},[3494],{"type":39,"tag":476,"props":3495,"children":3496},{"emptyLinePlaceholder":3302},[3497],{"type":44,"value":3305},{"type":39,"tag":476,"props":3499,"children":3500},{"class":478,"line":566},[3501],{"type":39,"tag":476,"props":3502,"children":3503},{},[3504],{"type":44,"value":3505},"\u002F\u002F ✅ Correct — gives access to SharedWithTeams and UnsharedFromTeams\n",{"type":39,"tag":476,"props":3507,"children":3508},{"class":478,"line":786},[3509],{"type":39,"tag":476,"props":3510,"children":3511},{},[3512],{"type":44,"value":3513},"var extended = turnContext.Activity.GetChannelData\u003CSharedChannelChannelData>();\n",{"type":39,"tag":476,"props":3515,"children":3516},{"class":478,"line":795},[3517],{"type":39,"tag":476,"props":3518,"children":3519},{},[3520],{"type":44,"value":3521},"var sharedWith = extended?.SharedWithTeams ?? new List\u003CTeamInfoEx>();\n",{"type":39,"tag":476,"props":3523,"children":3524},{"class":478,"line":22},[3525],{"type":39,"tag":476,"props":3526,"children":3527},{},[3528],{"type":44,"value":3529},"var unsharedFrom = extended?.UnsharedFromTeams ?? new List\u003CTeamInfoEx>();\n",{"type":39,"tag":40,"props":3531,"children":3532},{},[3533,3535,3541,3543,3549,3550,3556,3557,3563,3565,3571],{"type":44,"value":3534},"Each ",{"type":39,"tag":67,"props":3536,"children":3538},{"className":3537},[],[3539],{"type":44,"value":3540},"TeamInfoEx",{"type":44,"value":3542}," in the list contains ",{"type":39,"tag":67,"props":3544,"children":3546},{"className":3545},[],[3547],{"type":44,"value":3548},"Id",{"type":44,"value":662},{"type":39,"tag":67,"props":3551,"children":3553},{"className":3552},[],[3554],{"type":44,"value":3555},"Name",{"type":44,"value":662},{"type":39,"tag":67,"props":3558,"children":3560},{"className":3559},[],[3561],{"type":44,"value":3562},"AadGroupId",{"type":44,"value":3564},", and ",{"type":39,"tag":67,"props":3566,"children":3568},{"className":3567},[],[3569],{"type":44,"value":3570},"TenantId",{"type":44,"value":3572}," — use these to update per-team state and handle cross-tenant scenarios.",{"type":39,"tag":40,"props":3574,"children":3575},{},[3576,3588],{"type":39,"tag":47,"props":3577,"children":3578},{},[3579,3581,3586],{"type":44,"value":3580},"If only ",{"type":39,"tag":67,"props":3582,"children":3584},{"className":3583},[],[3585],{"type":44,"value":3467},{"type":44,"value":3587}," is used:",{"type":44,"value":3589}," The bot cannot determine which teams the channel was shared with or unshared from, making it impossible to update membership tracking or routing state accurately.",{"type":39,"tag":100,"props":3591,"children":3592},{},[],{"type":39,"tag":424,"props":3594,"children":3596},{"id":3595},"bot-6-use-teams-sdk-200-microsoftteamssdk-microsoftteams-js-v2",[3597,3599,3605,3607,3613],{"type":44,"value":3598},"BOT-6 · Use Teams SDK ≥ 2.0.0 (",{"type":39,"tag":67,"props":3600,"children":3602},{"className":3601},[],[3603],{"type":44,"value":3604},"Microsoft.Teams.SDK",{"type":44,"value":3606}," \u002F ",{"type":39,"tag":67,"props":3608,"children":3610},{"className":3609},[],[3611],{"type":44,"value":3612},"@microsoft\u002Fteams-js",{"type":44,"value":3614}," v2)",{"type":39,"tag":40,"props":3616,"children":3617},{},[3618],{"type":39,"tag":47,"props":3619,"children":3620},{},[3621],{"type":44,"value":443},{"type":39,"tag":40,"props":3623,"children":3624},{},[3625,3627,3632,3633,3639,3640,3645,3646,3652,3654,3659,3661,3666],{"type":44,"value":3626},"NGC-aware APIs — ",{"type":39,"tag":67,"props":3628,"children":3630},{"className":3629},[],[3631],{"type":44,"value":3459},{"type":44,"value":662},{"type":39,"tag":67,"props":3634,"children":3636},{"className":3635},[],[3637],{"type":44,"value":3638},"AllMembers",{"type":44,"value":662},{"type":39,"tag":67,"props":3641,"children":3643},{"className":3642},[],[3644],{"type":44,"value":1551},{"type":44,"value":662},{"type":39,"tag":67,"props":3647,"children":3649},{"className":3648},[],[3650],{"type":44,"value":3651},"channel.ownerTenantId",{"type":44,"value":3653},", and the ",{"type":39,"tag":67,"props":3655,"children":3657},{"className":3656},[],[3658],{"type":44,"value":3193},{"type":44,"value":3660},"\u002F",{"type":39,"tag":67,"props":3662,"children":3664},{"className":3663},[],[3665],{"type":44,"value":3200},{"type":44,"value":3667}," event types — were introduced in version 2.0 of both the .NET and JavaScript Teams SDKs. Any version below 2.0 is missing these APIs entirely; calling them against an older SDK silently falls back to incomplete or incorrect behavior.",{"type":39,"tag":40,"props":3669,"children":3670},{},[3671],{"type":39,"tag":47,"props":3672,"children":3673},{},[3674,3676,3682,3683,3689,3690,3696],{"type":44,"value":3675},"For .NET projects — check ",{"type":39,"tag":67,"props":3677,"children":3679},{"className":3678},[],[3680],{"type":44,"value":3681},"*.csproj",{"type":44,"value":3606},{"type":39,"tag":67,"props":3684,"children":3686},{"className":3685},[],[3687],{"type":44,"value":3688},"packages.config",{"type":44,"value":3606},{"type":39,"tag":67,"props":3691,"children":3693},{"className":3692},[],[3694],{"type":44,"value":3695},"Directory.Packages.props",{"type":44,"value":510},{"type":39,"tag":465,"props":3698,"children":3702},{"className":3699,"code":3700,"language":3701,"meta":470,"style":470},"language-xml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003C!-- ❌ Too old — NGC APIs unavailable -->\n\u003CPackageReference Include=\"Microsoft.Teams.SDK\" Version=\"1.*\" \u002F>\n\n\u003C!-- ✅ Minimum required -->\n\u003CPackageReference Include=\"Microsoft.Teams.SDK\" Version=\"2.0.0\" \u002F>\n","xml",[3703],{"type":39,"tag":67,"props":3704,"children":3705},{"__ignoreMap":470},[3706,3714,3722,3729,3737],{"type":39,"tag":476,"props":3707,"children":3708},{"class":478,"line":479},[3709],{"type":39,"tag":476,"props":3710,"children":3711},{},[3712],{"type":44,"value":3713},"\u003C!-- ❌ Too old — NGC APIs unavailable -->\n",{"type":39,"tag":476,"props":3715,"children":3716},{"class":478,"line":26},[3717],{"type":39,"tag":476,"props":3718,"children":3719},{},[3720],{"type":44,"value":3721},"\u003CPackageReference Include=\"Microsoft.Teams.SDK\" Version=\"1.*\" \u002F>\n",{"type":39,"tag":476,"props":3723,"children":3724},{"class":478,"line":533},[3725],{"type":39,"tag":476,"props":3726,"children":3727},{"emptyLinePlaceholder":3302},[3728],{"type":44,"value":3305},{"type":39,"tag":476,"props":3730,"children":3731},{"class":478,"line":566},[3732],{"type":39,"tag":476,"props":3733,"children":3734},{},[3735],{"type":44,"value":3736},"\u003C!-- ✅ Minimum required -->\n",{"type":39,"tag":476,"props":3738,"children":3739},{"class":478,"line":786},[3740],{"type":39,"tag":476,"props":3741,"children":3742},{},[3743],{"type":44,"value":3744},"\u003CPackageReference Include=\"Microsoft.Teams.SDK\" Version=\"2.0.0\" \u002F>\n",{"type":39,"tag":40,"props":3746,"children":3747},{},[3748],{"type":39,"tag":47,"props":3749,"children":3750},{},[3751,3753,3759],{"type":44,"value":3752},"For JavaScript \u002F TypeScript projects — check ",{"type":39,"tag":67,"props":3754,"children":3756},{"className":3755},[],[3757],{"type":44,"value":3758},"package.json",{"type":44,"value":510},{"type":39,"tag":465,"props":3761,"children":3763},{"className":467,"code":3762,"language":469,"meta":470,"style":470},"\u002F\u002F ❌ Too old\n\"@microsoft\u002Fteams-js\": \"^1.x.x\"\n\n\u002F\u002F ✅ Minimum required\n\"@microsoft\u002Fteams-js\": \"^2.0.0\"\n",[3764],{"type":39,"tag":67,"props":3765,"children":3766},{"__ignoreMap":470},[3767,3775,3808,3815,3823],{"type":39,"tag":476,"props":3768,"children":3769},{"class":478,"line":479},[3770],{"type":39,"tag":476,"props":3771,"children":3772},{"style":1783},[3773],{"type":44,"value":3774},"\u002F\u002F ❌ Too old\n",{"type":39,"tag":476,"props":3776,"children":3777},{"class":478,"line":26},[3778,3782,3786,3790,3795,3799,3804],{"type":39,"tag":476,"props":3779,"children":3780},{"style":483},[3781],{"type":44,"value":505},{"type":39,"tag":476,"props":3783,"children":3784},{"style":518},[3785],{"type":44,"value":3612},{"type":39,"tag":476,"props":3787,"children":3788},{"style":483},[3789],{"type":44,"value":505},{"type":39,"tag":476,"props":3791,"children":3792},{"style":1688},[3793],{"type":44,"value":3794},": ",{"type":39,"tag":476,"props":3796,"children":3797},{"style":483},[3798],{"type":44,"value":505},{"type":39,"tag":476,"props":3800,"children":3801},{"style":518},[3802],{"type":44,"value":3803},"^1.x.x",{"type":39,"tag":476,"props":3805,"children":3806},{"style":483},[3807],{"type":44,"value":563},{"type":39,"tag":476,"props":3809,"children":3810},{"class":478,"line":533},[3811],{"type":39,"tag":476,"props":3812,"children":3813},{"emptyLinePlaceholder":3302},[3814],{"type":44,"value":3305},{"type":39,"tag":476,"props":3816,"children":3817},{"class":478,"line":566},[3818],{"type":39,"tag":476,"props":3819,"children":3820},{"style":1783},[3821],{"type":44,"value":3822},"\u002F\u002F ✅ Minimum required\n",{"type":39,"tag":476,"props":3824,"children":3825},{"class":478,"line":786},[3826,3830,3834,3838,3842,3846,3851],{"type":39,"tag":476,"props":3827,"children":3828},{"style":483},[3829],{"type":44,"value":505},{"type":39,"tag":476,"props":3831,"children":3832},{"style":518},[3833],{"type":44,"value":3612},{"type":39,"tag":476,"props":3835,"children":3836},{"style":483},[3837],{"type":44,"value":505},{"type":39,"tag":476,"props":3839,"children":3840},{"style":1688},[3841],{"type":44,"value":3794},{"type":39,"tag":476,"props":3843,"children":3844},{"style":483},[3845],{"type":44,"value":505},{"type":39,"tag":476,"props":3847,"children":3848},{"style":518},[3849],{"type":44,"value":3850},"^2.0.0",{"type":39,"tag":476,"props":3852,"children":3853},{"style":483},[3854],{"type":44,"value":563},{"type":39,"tag":40,"props":3856,"children":3857},{},[3858],{"type":39,"tag":47,"props":3859,"children":3860},{},[3861],{"type":44,"value":3862},"If below 2.0.0:",{"type":39,"tag":76,"props":3864,"children":3865},{},[3866,3889,3905,3915],{"type":39,"tag":80,"props":3867,"children":3868},{},[3869,3874,3876,3881,3882,3887],{"type":39,"tag":67,"props":3870,"children":3872},{"className":3871},[],[3873],{"type":44,"value":3459},{"type":44,"value":3875}," type is absent; ",{"type":39,"tag":67,"props":3877,"children":3879},{"className":3878},[],[3880],{"type":44,"value":3193},{"type":44,"value":3660},{"type":39,"tag":67,"props":3883,"children":3885},{"className":3884},[],[3886],{"type":44,"value":3200},{"type":44,"value":3888}," events cannot be parsed.",{"type":39,"tag":80,"props":3890,"children":3891},{},[3892,3897,3898,3903],{"type":39,"tag":67,"props":3893,"children":3895},{"className":3894},[],[3896],{"type":44,"value":1551},{"type":44,"value":2005},{"type":39,"tag":67,"props":3899,"children":3901},{"className":3900},[],[3902],{"type":44,"value":3651},{"type":44,"value":3904}," are not present in the context object.",{"type":39,"tag":80,"props":3906,"children":3907},{},[3908,3913],{"type":39,"tag":67,"props":3909,"children":3911},{"className":3910},[],[3912],{"type":44,"value":3638},{"type":44,"value":3914}," navigation property does not exist on the Graph SDK fluent client.",{"type":39,"tag":80,"props":3916,"children":3917},{},[3918],{"type":44,"value":3919},"All BOT-3, BOT-5, CTX-1, CTX-2, and MEM-5 fixes depend on this upgrade being in place first.",{"type":39,"tag":100,"props":3921,"children":3922},{},[],{"type":39,"tag":55,"props":3924,"children":3926},{"id":3925},"auth-authentication-external-user-rules",[3927],{"type":44,"value":3928},"AUTH — Authentication & External User Rules",{"type":39,"tag":424,"props":3930,"children":3932},{"id":3931},"auth-1-use-hosttenantid-ownertenantid-for-sharepoint-token-requests",[3933,3935,3941,3942,3948],{"type":44,"value":3934},"AUTH-1 · Use ",{"type":39,"tag":67,"props":3936,"children":3938},{"className":3937},[],[3939],{"type":44,"value":3940},"hostTenantId",{"type":44,"value":3606},{"type":39,"tag":67,"props":3943,"children":3945},{"className":3944},[],[3946],{"type":44,"value":3947},"ownerTenantId",{"type":44,"value":3949}," for SharePoint token requests",{"type":39,"tag":40,"props":3951,"children":3952},{},[3953],{"type":39,"tag":47,"props":3954,"children":3955},{},[3956],{"type":44,"value":443},{"type":39,"tag":40,"props":3958,"children":3959},{},[3960,3962,3968,3970,3975],{"type":44,"value":3961},"External (cross-tenant) users reside in their home tenant. When they access SharePoint content in the host channel's site, the ",{"type":39,"tag":67,"props":3963,"children":3965},{"className":3964},[],[3966],{"type":44,"value":3967},"getAuthToken",{"type":44,"value":3969}," call must use the ",{"type":39,"tag":47,"props":3971,"children":3972},{},[3973],{"type":44,"value":3974},"host tenant ID",{"type":44,"value":3976},", not the user's own tenant ID.",{"type":39,"tag":40,"props":3978,"children":3979},{},[3980],{"type":39,"tag":47,"props":3981,"children":3982},{},[3983],{"type":44,"value":3984},"For tabs (JS SDK v2):",{"type":39,"tag":465,"props":3986,"children":3988},{"className":1671,"code":3987,"language":1673,"meta":470,"style":470},"const context = await microsoftTeams.app.getContext();\nconst hostTenantId = context.channel.ownerTenantId;\n",[3989],{"type":39,"tag":67,"props":3990,"children":3991},{"__ignoreMap":470},[3992,4039],{"type":39,"tag":476,"props":3993,"children":3994},{"class":478,"line":479},[3995,3999,4003,4007,4011,4015,4019,4023,4027,4031,4035],{"type":39,"tag":476,"props":3996,"children":3997},{"style":497},[3998],{"type":44,"value":1685},{"type":39,"tag":476,"props":4000,"children":4001},{"style":1688},[4002],{"type":44,"value":1691},{"type":39,"tag":476,"props":4004,"children":4005},{"style":483},[4006],{"type":44,"value":1696},{"type":39,"tag":476,"props":4008,"children":4009},{"style":1699},[4010],{"type":44,"value":1702},{"type":39,"tag":476,"props":4012,"children":4013},{"style":1688},[4014],{"type":44,"value":1707},{"type":39,"tag":476,"props":4016,"children":4017},{"style":483},[4018],{"type":44,"value":1712},{"type":39,"tag":476,"props":4020,"children":4021},{"style":1688},[4022],{"type":44,"value":1717},{"type":39,"tag":476,"props":4024,"children":4025},{"style":483},[4026],{"type":44,"value":1712},{"type":39,"tag":476,"props":4028,"children":4029},{"style":1724},[4030],{"type":44,"value":1727},{"type":39,"tag":476,"props":4032,"children":4033},{"style":1688},[4034],{"type":44,"value":1732},{"type":39,"tag":476,"props":4036,"children":4037},{"style":483},[4038],{"type":44,"value":1737},{"type":39,"tag":476,"props":4040,"children":4041},{"class":478,"line":26},[4042,4046,4051,4055,4059,4063,4067,4071,4075],{"type":39,"tag":476,"props":4043,"children":4044},{"style":497},[4045],{"type":44,"value":1685},{"type":39,"tag":476,"props":4047,"children":4048},{"style":1688},[4049],{"type":44,"value":4050}," hostTenantId ",{"type":39,"tag":476,"props":4052,"children":4053},{"style":483},[4054],{"type":44,"value":1696},{"type":39,"tag":476,"props":4056,"children":4057},{"style":1688},[4058],{"type":44,"value":1758},{"type":39,"tag":476,"props":4060,"children":4061},{"style":483},[4062],{"type":44,"value":1712},{"type":39,"tag":476,"props":4064,"children":4065},{"style":1688},[4066],{"type":44,"value":1913},{"type":39,"tag":476,"props":4068,"children":4069},{"style":483},[4070],{"type":44,"value":1712},{"type":39,"tag":476,"props":4072,"children":4073},{"style":1688},[4074],{"type":44,"value":3947},{"type":39,"tag":476,"props":4076,"children":4077},{"style":483},[4078],{"type":44,"value":1737},{"type":39,"tag":40,"props":4080,"children":4081},{},[4082],{"type":39,"tag":47,"props":4083,"children":4084},{},[4085],{"type":44,"value":4086},"For bots (Agent SDK \u002F Bot Framework):",{"type":39,"tag":465,"props":4088,"children":4090},{"className":3274,"code":4089,"language":3276,"meta":470,"style":470},"var hostTenantId = turnContext.Activity.Conversation.TenantId;\n",[4091],{"type":39,"tag":67,"props":4092,"children":4093},{"__ignoreMap":470},[4094],{"type":39,"tag":476,"props":4095,"children":4096},{"class":478,"line":479},[4097],{"type":39,"tag":476,"props":4098,"children":4099},{},[4100],{"type":44,"value":4089},{"type":39,"tag":40,"props":4102,"children":4103},{},[4104,4106,4111,4113,4118,4120,4125],{"type":44,"value":4105},"Pass ",{"type":39,"tag":67,"props":4107,"children":4109},{"className":4108},[],[4110],{"type":44,"value":3940},{"type":44,"value":4112}," in the ",{"type":39,"tag":67,"props":4114,"children":4116},{"className":4115},[],[4117],{"type":44,"value":2262},{"type":44,"value":4119}," parameter of ",{"type":39,"tag":67,"props":4121,"children":4123},{"className":4122},[],[4124],{"type":44,"value":3967},{"type":44,"value":4126}," so SPO grants the correct cross-tenant access.",{"type":39,"tag":40,"props":4128,"children":4129},{},[4130,4134],{"type":39,"tag":47,"props":4131,"children":4132},{},[4133],{"type":44,"value":1493},{"type":44,"value":4135}," External users get token errors or access-denied when opening tab content backed by SharePoint.",{"type":39,"tag":100,"props":4137,"children":4138},{},[],{"type":39,"tag":424,"props":4140,"children":4142},{"id":4141},"auth-2-do-not-block-users-based-on-unrecognized-tenant-ids",[4143],{"type":44,"value":4144},"AUTH-2 · Do not block users based on unrecognized tenant IDs",{"type":39,"tag":40,"props":4146,"children":4147},{},[4148],{"type":39,"tag":47,"props":4149,"children":4150},{},[4151],{"type":44,"value":625},{"type":39,"tag":40,"props":4153,"children":4154},{},[4155,4157,4162,4164,4169],{"type":44,"value":4156},"Shared channels support cross-tenant members. Their ",{"type":39,"tag":67,"props":4158,"children":4160},{"className":4159},[],[4161],{"type":44,"value":2262},{"type":44,"value":4163}," will not match the host's ",{"type":39,"tag":67,"props":4165,"children":4167},{"className":4166},[],[4168],{"type":44,"value":3947},{"type":44,"value":4170},". Treating unknown tenant IDs as \"invalid\" accidentally blocks legitimate external collaborators.",{"type":39,"tag":40,"props":4172,"children":4173},{},[4174,4176,4182,4184,4189],{"type":44,"value":4175},"Do not assume all users are from the host tenant. Compare ",{"type":39,"tag":67,"props":4177,"children":4179},{"className":4178},[],[4180],{"type":44,"value":4181},"user.tenant.id",{"type":44,"value":4183}," (the current user's tenant) with ",{"type":39,"tag":67,"props":4185,"children":4187},{"className":4186},[],[4188],{"type":44,"value":3651},{"type":44,"value":4190}," (the host tenant) to detect external users. If they differ, the user is external — not invalid.",{"type":39,"tag":465,"props":4192,"children":4194},{"className":1671,"code":4193,"language":1673,"meta":470,"style":470},"const context = await microsoftTeams.app.getContext();\nconst isExternal = context.user.tenant.id !== context.channel.ownerTenantId;\n",[4195],{"type":39,"tag":67,"props":4196,"children":4197},{"__ignoreMap":470},[4198,4245],{"type":39,"tag":476,"props":4199,"children":4200},{"class":478,"line":479},[4201,4205,4209,4213,4217,4221,4225,4229,4233,4237,4241],{"type":39,"tag":476,"props":4202,"children":4203},{"style":497},[4204],{"type":44,"value":1685},{"type":39,"tag":476,"props":4206,"children":4207},{"style":1688},[4208],{"type":44,"value":1691},{"type":39,"tag":476,"props":4210,"children":4211},{"style":483},[4212],{"type":44,"value":1696},{"type":39,"tag":476,"props":4214,"children":4215},{"style":1699},[4216],{"type":44,"value":1702},{"type":39,"tag":476,"props":4218,"children":4219},{"style":1688},[4220],{"type":44,"value":1707},{"type":39,"tag":476,"props":4222,"children":4223},{"style":483},[4224],{"type":44,"value":1712},{"type":39,"tag":476,"props":4226,"children":4227},{"style":1688},[4228],{"type":44,"value":1717},{"type":39,"tag":476,"props":4230,"children":4231},{"style":483},[4232],{"type":44,"value":1712},{"type":39,"tag":476,"props":4234,"children":4235},{"style":1724},[4236],{"type":44,"value":1727},{"type":39,"tag":476,"props":4238,"children":4239},{"style":1688},[4240],{"type":44,"value":1732},{"type":39,"tag":476,"props":4242,"children":4243},{"style":483},[4244],{"type":44,"value":1737},{"type":39,"tag":476,"props":4246,"children":4247},{"class":478,"line":26},[4248,4252,4257,4261,4265,4269,4274,4278,4283,4287,4292,4297,4301,4305,4309,4313,4317],{"type":39,"tag":476,"props":4249,"children":4250},{"style":497},[4251],{"type":44,"value":1685},{"type":39,"tag":476,"props":4253,"children":4254},{"style":1688},[4255],{"type":44,"value":4256}," isExternal ",{"type":39,"tag":476,"props":4258,"children":4259},{"style":483},[4260],{"type":44,"value":1696},{"type":39,"tag":476,"props":4262,"children":4263},{"style":1688},[4264],{"type":44,"value":1758},{"type":39,"tag":476,"props":4266,"children":4267},{"style":483},[4268],{"type":44,"value":1712},{"type":39,"tag":476,"props":4270,"children":4271},{"style":1688},[4272],{"type":44,"value":4273},"user",{"type":39,"tag":476,"props":4275,"children":4276},{"style":483},[4277],{"type":44,"value":1712},{"type":39,"tag":476,"props":4279,"children":4280},{"style":1688},[4281],{"type":44,"value":4282},"tenant",{"type":39,"tag":476,"props":4284,"children":4285},{"style":483},[4286],{"type":44,"value":1712},{"type":39,"tag":476,"props":4288,"children":4289},{"style":1688},[4290],{"type":44,"value":4291},"id ",{"type":39,"tag":476,"props":4293,"children":4294},{"style":483},[4295],{"type":44,"value":4296},"!==",{"type":39,"tag":476,"props":4298,"children":4299},{"style":1688},[4300],{"type":44,"value":1758},{"type":39,"tag":476,"props":4302,"children":4303},{"style":483},[4304],{"type":44,"value":1712},{"type":39,"tag":476,"props":4306,"children":4307},{"style":1688},[4308],{"type":44,"value":1913},{"type":39,"tag":476,"props":4310,"children":4311},{"style":483},[4312],{"type":44,"value":1712},{"type":39,"tag":476,"props":4314,"children":4315},{"style":1688},[4316],{"type":44,"value":3947},{"type":39,"tag":476,"props":4318,"children":4319},{"style":483},[4320],{"type":44,"value":1737},{"type":39,"tag":100,"props":4322,"children":4323},{},[],{"type":39,"tag":424,"props":4325,"children":4327},{"id":4326},"auth-3-detect-guest-users-via-roles-property-not-heuristics",[4328,4330,4335],{"type":44,"value":4329},"AUTH-3 · Detect guest users via ",{"type":39,"tag":67,"props":4331,"children":4333},{"className":4332},[],[4334],{"type":44,"value":2182},{"type":44,"value":4336}," property, not heuristics",{"type":39,"tag":40,"props":4338,"children":4339},{},[4340],{"type":39,"tag":47,"props":4341,"children":4342},{},[4343],{"type":44,"value":2043},{"type":39,"tag":40,"props":4345,"children":4346},{},[4347,4349,4354,4356,4361],{"type":44,"value":4348},"To reliably identify guests, check the ",{"type":39,"tag":67,"props":4350,"children":4352},{"className":4351},[],[4353],{"type":44,"value":2182},{"type":44,"value":4355}," field in the ",{"type":39,"tag":67,"props":4357,"children":4359},{"className":4358},[],[4360],{"type":44,"value":660},{"type":44,"value":4362}," response:",{"type":39,"tag":465,"props":4364,"children":4365},{"className":1107,"code":1108,"language":1109,"meta":470,"style":470},[4366],{"type":39,"tag":67,"props":4367,"children":4368},{"__ignoreMap":470},[4369],{"type":39,"tag":476,"props":4370,"children":4371},{"class":478,"line":479},[4372],{"type":39,"tag":476,"props":4373,"children":4374},{},[4375],{"type":44,"value":1108},{"type":39,"tag":40,"props":4377,"children":4378},{},[4379,4381],{"type":44,"value":4380},"For guests: ",{"type":39,"tag":67,"props":4382,"children":4384},{"className":4383},[],[4385],{"type":44,"value":4386},"\"roles\": [\"guest\"]",{"type":39,"tag":40,"props":4388,"children":4389},{},[4390],{"type":44,"value":4391},"Do not rely on display name patterns, email domain heuristics, or upstream session flags.",{"type":39,"tag":100,"props":4393,"children":4394},{},[],{"type":39,"tag":55,"props":4396,"children":4398},{"id":4397},"data-data-scoping-rules",[4399],{"type":44,"value":4400},"DATA — Data Scoping Rules",{"type":39,"tag":424,"props":4402,"children":4404},{"id":4403},"data-1-never-include-private-channel-data-in-team-wide-reports-or-dashboards",[4405],{"type":44,"value":4406},"DATA-1 · Never include private channel data in team-wide reports or dashboards",{"type":39,"tag":40,"props":4408,"children":4409},{},[4410],{"type":39,"tag":47,"props":4411,"children":4412},{},[4413],{"type":44,"value":443},{"type":39,"tag":40,"props":4415,"children":4416},{},[4417],{"type":44,"value":4418},"Private channel content is restricted to channel members. Aggregating it into a team-wide view exposes sensitive data to users who do not have access.",{"type":39,"tag":40,"props":4420,"children":4421},{},[4422,4427],{"type":39,"tag":47,"props":4423,"children":4424},{},[4425],{"type":44,"value":4426},"Rule:",{"type":44,"value":4428}," Scope all data reads, reports, and analytics to the channel context in which the app is running.",{"type":39,"tag":100,"props":4430,"children":4431},{},[],{"type":39,"tag":424,"props":4433,"children":4435},{"id":4434},"data-2-do-not-cross-post-or-broadcast-across-channels-assuming-shared-team-membership",[4436],{"type":44,"value":4437},"DATA-2 · Do not cross-post or broadcast across channels assuming shared team membership",{"type":39,"tag":40,"props":4439,"children":4440},{},[4441],{"type":39,"tag":47,"props":4442,"children":4443},{},[4444],{"type":44,"value":625},{"type":39,"tag":40,"props":4446,"children":4447},{},[4448],{"type":44,"value":4449},"Broadcasting content from one channel to others, or generating links that span channels, assumes all team members see all channels — which is false for shared and private channels.",{"type":39,"tag":40,"props":4451,"children":4452},{},[4453,4457],{"type":39,"tag":47,"props":4454,"children":4455},{},[4456],{"type":44,"value":4426},{"type":44,"value":4458}," Cross-channel operations require explicit permission checks per channel.",{"type":39,"tag":100,"props":4460,"children":4461},{},[],{"type":39,"tag":424,"props":4463,"children":4465},{"id":4464},"data-3-do-not-rely-on-channeltype-or-membershiptype-for-app-behavior-logic",[4466,4468,4474,4475,4481],{"type":44,"value":4467},"DATA-3 · Do not rely on ",{"type":39,"tag":67,"props":4469,"children":4471},{"className":4470},[],[4472],{"type":44,"value":4473},"channelType",{"type":44,"value":1952},{"type":39,"tag":67,"props":4476,"children":4478},{"className":4477},[],[4479],{"type":44,"value":4480},"membershipType",{"type":44,"value":4482}," for app behavior logic",{"type":39,"tag":40,"props":4484,"children":4485},{},[4486],{"type":39,"tag":47,"props":4487,"children":4488},{},[4489],{"type":44,"value":443},{"type":39,"tag":40,"props":4491,"children":4492},{},[4493,4495,4500,4501,4506],{"type":44,"value":4494},"Channel type values are subject to change as the Teams platform evolves. App logic that branches on ",{"type":39,"tag":67,"props":4496,"children":4498},{"className":4497},[],[4499],{"type":44,"value":4480},{"type":44,"value":1952},{"type":39,"tag":67,"props":4502,"children":4504},{"className":4503},[],[4505],{"type":44,"value":4473},{"type":44,"value":4507}," will become brittle.",{"type":39,"tag":40,"props":4509,"children":4510},{},[4511,4515,4517,4522,4523,4528,4529,4534],{"type":39,"tag":47,"props":4512,"children":4513},{},[4514],{"type":44,"value":1095},{"type":44,"value":4516}," Use capability-based APIs (",{"type":39,"tag":67,"props":4518,"children":4520},{"className":4519},[],[4521],{"type":44,"value":660},{"type":44,"value":662},{"type":39,"tag":67,"props":4524,"children":4526},{"className":4525},[],[4527],{"type":44,"value":689},{"type":44,"value":662},{"type":39,"tag":67,"props":4530,"children":4532},{"className":4531},[],[4533],{"type":44,"value":1449},{"type":44,"value":4535},") to determine behavior, not channel-type discrimination.",{"type":39,"tag":111,"props":4537,"children":4538},{},[4539],{"type":39,"tag":40,"props":4540,"children":4541},{},[4542,4547,4549,4554,4556,4561],{"type":39,"tag":47,"props":4543,"children":4544},{},[4545],{"type":44,"value":4546},"Note:",{"type":44,"value":4548}," Reading ",{"type":39,"tag":67,"props":4550,"children":4552},{"className":4551},[],[4553],{"type":44,"value":4480},{"type":44,"value":4555}," for ",{"type":39,"tag":47,"props":4557,"children":4558},{},[4559],{"type":44,"value":4560},"display-only",{"type":44,"value":4562}," purposes (e.g., showing a label) is acceptable. The rule applies to branching app logic on these values.",{"type":39,"tag":100,"props":4564,"children":4565},{},[],{"type":39,"tag":55,"props":4567,"children":4569},{"id":4568},"sub-graph-subscription-rules",[4570],{"type":44,"value":4571},"SUB — Graph Subscription Rules",{"type":39,"tag":424,"props":4573,"children":4575},{"id":4574},"sub-1-use-correct-resource-path-and-include-notifyonindirectmembershipupdatetrue",[4576,4578],{"type":44,"value":4577},"SUB-1 · Use correct resource path and include ",{"type":39,"tag":67,"props":4579,"children":4581},{"className":4580},[],[4582],{"type":44,"value":4583},"notifyOnIndirectMembershipUpdate=true",{"type":39,"tag":40,"props":4585,"children":4586},{},[4587],{"type":39,"tag":47,"props":4588,"children":4589},{},[4590],{"type":44,"value":625},{"type":39,"tag":40,"props":4592,"children":4593},{},[4594],{"type":44,"value":4595},"The correct resource path for subscribing to ALL private and shared channel membership changes in a team is:",{"type":39,"tag":465,"props":4597,"children":4599},{"className":1107,"code":4598,"language":1109,"meta":470,"style":470},"\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers\n",[4600],{"type":39,"tag":67,"props":4601,"children":4602},{"__ignoreMap":470},[4603],{"type":39,"tag":476,"props":4604,"children":4605},{"class":478,"line":479},[4606],{"type":39,"tag":476,"props":4607,"children":4608},{},[4609],{"type":44,"value":4598},{"type":39,"tag":40,"props":4611,"children":4612},{},[4613,4615,4620],{"type":44,"value":4614},"To also receive ",{"type":39,"tag":47,"props":4616,"children":4617},{},[4618],{"type":44,"value":4619},"indirect",{"type":44,"value":4621}," membership updates (from shared teams), add both query parameters:",{"type":39,"tag":465,"props":4623,"children":4625},{"className":1107,"code":4624,"language":1109,"meta":470,"style":470},"\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers?notifyOnIndirectMembershipUpdate=true&suppressNotificationWhenSharedUnsharedWithTeam=true\n",[4626],{"type":39,"tag":67,"props":4627,"children":4628},{"__ignoreMap":470},[4629],{"type":39,"tag":476,"props":4630,"children":4631},{"class":478,"line":479},[4632],{"type":39,"tag":476,"props":4633,"children":4634},{},[4635],{"type":44,"value":4624},{"type":39,"tag":40,"props":4637,"children":4638},{},[4639],{"type":44,"value":4640},"Full subscription request:",{"type":39,"tag":465,"props":4642,"children":4644},{"className":467,"code":4643,"language":469,"meta":470,"style":470},"{\n  \"changeType\": \"created,deleted,updated\",\n  \"notificationUrl\": \"https:\u002F\u002Fyour-webhook\u002Fapi\u002Fnotifications\",\n  \"resource\": \"\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers?notifyOnIndirectMembershipUpdate=true&suppressNotificationWhenSharedUnsharedWithTeam=true\",\n  \"includeResourceData\": true,\n  \"expirationDateTime\": \"2024-09-19T11:00:00.0000000Z\"\n}\n",[4645],{"type":39,"tag":67,"props":4646,"children":4647},{"__ignoreMap":470},[4648,4655,4692,4729,4766,4791,4824],{"type":39,"tag":476,"props":4649,"children":4650},{"class":478,"line":479},[4651],{"type":39,"tag":476,"props":4652,"children":4653},{"style":483},[4654],{"type":44,"value":486},{"type":39,"tag":476,"props":4656,"children":4657},{"class":478,"line":26},[4658,4662,4667,4671,4675,4679,4684,4688],{"type":39,"tag":476,"props":4659,"children":4660},{"style":483},[4661],{"type":44,"value":494},{"type":39,"tag":476,"props":4663,"children":4664},{"style":497},[4665],{"type":44,"value":4666},"changeType",{"type":39,"tag":476,"props":4668,"children":4669},{"style":483},[4670],{"type":44,"value":505},{"type":39,"tag":476,"props":4672,"children":4673},{"style":483},[4674],{"type":44,"value":510},{"type":39,"tag":476,"props":4676,"children":4677},{"style":483},[4678],{"type":44,"value":515},{"type":39,"tag":476,"props":4680,"children":4681},{"style":518},[4682],{"type":44,"value":4683},"created,deleted,updated",{"type":39,"tag":476,"props":4685,"children":4686},{"style":483},[4687],{"type":44,"value":505},{"type":39,"tag":476,"props":4689,"children":4690},{"style":483},[4691],{"type":44,"value":530},{"type":39,"tag":476,"props":4693,"children":4694},{"class":478,"line":533},[4695,4699,4704,4708,4712,4716,4721,4725],{"type":39,"tag":476,"props":4696,"children":4697},{"style":483},[4698],{"type":44,"value":494},{"type":39,"tag":476,"props":4700,"children":4701},{"style":497},[4702],{"type":44,"value":4703},"notificationUrl",{"type":39,"tag":476,"props":4705,"children":4706},{"style":483},[4707],{"type":44,"value":505},{"type":39,"tag":476,"props":4709,"children":4710},{"style":483},[4711],{"type":44,"value":510},{"type":39,"tag":476,"props":4713,"children":4714},{"style":483},[4715],{"type":44,"value":515},{"type":39,"tag":476,"props":4717,"children":4718},{"style":518},[4719],{"type":44,"value":4720},"https:\u002F\u002Fyour-webhook\u002Fapi\u002Fnotifications",{"type":39,"tag":476,"props":4722,"children":4723},{"style":483},[4724],{"type":44,"value":505},{"type":39,"tag":476,"props":4726,"children":4727},{"style":483},[4728],{"type":44,"value":530},{"type":39,"tag":476,"props":4730,"children":4731},{"class":478,"line":566},[4732,4736,4741,4745,4749,4753,4758,4762],{"type":39,"tag":476,"props":4733,"children":4734},{"style":483},[4735],{"type":44,"value":494},{"type":39,"tag":476,"props":4737,"children":4738},{"style":497},[4739],{"type":44,"value":4740},"resource",{"type":39,"tag":476,"props":4742,"children":4743},{"style":483},[4744],{"type":44,"value":505},{"type":39,"tag":476,"props":4746,"children":4747},{"style":483},[4748],{"type":44,"value":510},{"type":39,"tag":476,"props":4750,"children":4751},{"style":483},[4752],{"type":44,"value":515},{"type":39,"tag":476,"props":4754,"children":4755},{"style":518},[4756],{"type":44,"value":4757},"\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers?notifyOnIndirectMembershipUpdate=true&suppressNotificationWhenSharedUnsharedWithTeam=true",{"type":39,"tag":476,"props":4759,"children":4760},{"style":483},[4761],{"type":44,"value":505},{"type":39,"tag":476,"props":4763,"children":4764},{"style":483},[4765],{"type":44,"value":530},{"type":39,"tag":476,"props":4767,"children":4768},{"class":478,"line":786},[4769,4773,4778,4782,4786],{"type":39,"tag":476,"props":4770,"children":4771},{"style":483},[4772],{"type":44,"value":494},{"type":39,"tag":476,"props":4774,"children":4775},{"style":497},[4776],{"type":44,"value":4777},"includeResourceData",{"type":39,"tag":476,"props":4779,"children":4780},{"style":483},[4781],{"type":44,"value":505},{"type":39,"tag":476,"props":4783,"children":4784},{"style":483},[4785],{"type":44,"value":510},{"type":39,"tag":476,"props":4787,"children":4788},{"style":483},[4789],{"type":44,"value":4790}," true,\n",{"type":39,"tag":476,"props":4792,"children":4793},{"class":478,"line":795},[4794,4798,4803,4807,4811,4815,4820],{"type":39,"tag":476,"props":4795,"children":4796},{"style":483},[4797],{"type":44,"value":494},{"type":39,"tag":476,"props":4799,"children":4800},{"style":497},[4801],{"type":44,"value":4802},"expirationDateTime",{"type":39,"tag":476,"props":4804,"children":4805},{"style":483},[4806],{"type":44,"value":505},{"type":39,"tag":476,"props":4808,"children":4809},{"style":483},[4810],{"type":44,"value":510},{"type":39,"tag":476,"props":4812,"children":4813},{"style":483},[4814],{"type":44,"value":515},{"type":39,"tag":476,"props":4816,"children":4817},{"style":518},[4818],{"type":44,"value":4819},"2024-09-19T11:00:00.0000000Z",{"type":39,"tag":476,"props":4821,"children":4822},{"style":483},[4823],{"type":44,"value":563},{"type":39,"tag":476,"props":4825,"children":4826},{"class":478,"line":22},[4827],{"type":39,"tag":476,"props":4828,"children":4829},{"style":483},[4830],{"type":44,"value":572},{"type":39,"tag":40,"props":4832,"children":4833},{},[4834,4845],{"type":39,"tag":47,"props":4835,"children":4836},{},[4837,4839,4844],{"type":44,"value":4838},"Without ",{"type":39,"tag":67,"props":4840,"children":4842},{"className":4841},[],[4843],{"type":44,"value":4583},{"type":44,"value":510},{"type":44,"value":4846}," Only direct membership changes fire. Indirect changes (from shared teams) are silently missed, causing roster drift.",{"type":39,"tag":40,"props":4848,"children":4849},{},[4850,4861,4863,4869],{"type":39,"tag":47,"props":4851,"children":4852},{},[4853,4854,4860],{"type":44,"value":4838},{"type":39,"tag":67,"props":4855,"children":4857},{"className":4856},[],[4858],{"type":44,"value":4859},"suppressNotificationWhenSharedUnsharedWithTeam=true",{"type":44,"value":510},{"type":44,"value":4862}," Individual per-user notifications fire when a channel is shared\u002Funshared with a large team, causing thousands of events. Always pair this with ",{"type":39,"tag":67,"props":4864,"children":4866},{"className":4865},[],[4867],{"type":44,"value":4868},"SUB-2",{"type":44,"value":1712},{"type":39,"tag":100,"props":4871,"children":4872},{},[],{"type":39,"tag":424,"props":4874,"children":4876},{"id":4875},"sub-2-use-sharedwithteams-subscription-for-bulk-shareunshare-events",[4877,4879,4885],{"type":44,"value":4878},"SUB-2 · Use ",{"type":39,"tag":67,"props":4880,"children":4882},{"className":4881},[],[4883],{"type":44,"value":4884},"sharedWithTeams",{"type":44,"value":4886}," subscription for bulk share\u002Funshare events",{"type":39,"tag":40,"props":4888,"children":4889},{},[4890],{"type":39,"tag":47,"props":4891,"children":4892},{},[4893],{"type":44,"value":625},{"type":39,"tag":40,"props":4895,"children":4896},{},[4897,4899,4904,4906,4911],{"type":44,"value":4898},"When a shared channel is added to or removed from a large team, per-user notifications can number in the thousands. Subscribe to ",{"type":39,"tag":67,"props":4900,"children":4902},{"className":4901},[],[4903],{"type":44,"value":4884},{"type":44,"value":4905}," for a single share\u002Funshare event, then refresh the full member list via ",{"type":39,"tag":67,"props":4907,"children":4909},{"className":4908},[],[4910],{"type":44,"value":660},{"type":44,"value":1712},{"type":39,"tag":465,"props":4913,"children":4915},{"className":467,"code":4914,"language":469,"meta":470,"style":470},"{\n  \"changeType\": \"created,deleted\",\n  \"notificationUrl\": \"https:\u002F\u002Fyour-webhook\u002Fapi\u002Fnotifications\",\n  \"resource\": \"\u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FsharedWithTeams\",\n  \"includeResourceData\": true,\n  \"expirationDateTime\": \"2024-09-19T11:00:00.0000000Z\"\n}\n",[4916],{"type":39,"tag":67,"props":4917,"children":4918},{"__ignoreMap":470},[4919,4926,4962,4997,5033,5056,5087],{"type":39,"tag":476,"props":4920,"children":4921},{"class":478,"line":479},[4922],{"type":39,"tag":476,"props":4923,"children":4924},{"style":483},[4925],{"type":44,"value":486},{"type":39,"tag":476,"props":4927,"children":4928},{"class":478,"line":26},[4929,4933,4937,4941,4945,4949,4954,4958],{"type":39,"tag":476,"props":4930,"children":4931},{"style":483},[4932],{"type":44,"value":494},{"type":39,"tag":476,"props":4934,"children":4935},{"style":497},[4936],{"type":44,"value":4666},{"type":39,"tag":476,"props":4938,"children":4939},{"style":483},[4940],{"type":44,"value":505},{"type":39,"tag":476,"props":4942,"children":4943},{"style":483},[4944],{"type":44,"value":510},{"type":39,"tag":476,"props":4946,"children":4947},{"style":483},[4948],{"type":44,"value":515},{"type":39,"tag":476,"props":4950,"children":4951},{"style":518},[4952],{"type":44,"value":4953},"created,deleted",{"type":39,"tag":476,"props":4955,"children":4956},{"style":483},[4957],{"type":44,"value":505},{"type":39,"tag":476,"props":4959,"children":4960},{"style":483},[4961],{"type":44,"value":530},{"type":39,"tag":476,"props":4963,"children":4964},{"class":478,"line":533},[4965,4969,4973,4977,4981,4985,4989,4993],{"type":39,"tag":476,"props":4966,"children":4967},{"style":483},[4968],{"type":44,"value":494},{"type":39,"tag":476,"props":4970,"children":4971},{"style":497},[4972],{"type":44,"value":4703},{"type":39,"tag":476,"props":4974,"children":4975},{"style":483},[4976],{"type":44,"value":505},{"type":39,"tag":476,"props":4978,"children":4979},{"style":483},[4980],{"type":44,"value":510},{"type":39,"tag":476,"props":4982,"children":4983},{"style":483},[4984],{"type":44,"value":515},{"type":39,"tag":476,"props":4986,"children":4987},{"style":518},[4988],{"type":44,"value":4720},{"type":39,"tag":476,"props":4990,"children":4991},{"style":483},[4992],{"type":44,"value":505},{"type":39,"tag":476,"props":4994,"children":4995},{"style":483},[4996],{"type":44,"value":530},{"type":39,"tag":476,"props":4998,"children":4999},{"class":478,"line":566},[5000,5004,5008,5012,5016,5020,5025,5029],{"type":39,"tag":476,"props":5001,"children":5002},{"style":483},[5003],{"type":44,"value":494},{"type":39,"tag":476,"props":5005,"children":5006},{"style":497},[5007],{"type":44,"value":4740},{"type":39,"tag":476,"props":5009,"children":5010},{"style":483},[5011],{"type":44,"value":505},{"type":39,"tag":476,"props":5013,"children":5014},{"style":483},[5015],{"type":44,"value":510},{"type":39,"tag":476,"props":5017,"children":5018},{"style":483},[5019],{"type":44,"value":515},{"type":39,"tag":476,"props":5021,"children":5022},{"style":518},[5023],{"type":44,"value":5024},"\u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002FsharedWithTeams",{"type":39,"tag":476,"props":5026,"children":5027},{"style":483},[5028],{"type":44,"value":505},{"type":39,"tag":476,"props":5030,"children":5031},{"style":483},[5032],{"type":44,"value":530},{"type":39,"tag":476,"props":5034,"children":5035},{"class":478,"line":786},[5036,5040,5044,5048,5052],{"type":39,"tag":476,"props":5037,"children":5038},{"style":483},[5039],{"type":44,"value":494},{"type":39,"tag":476,"props":5041,"children":5042},{"style":497},[5043],{"type":44,"value":4777},{"type":39,"tag":476,"props":5045,"children":5046},{"style":483},[5047],{"type":44,"value":505},{"type":39,"tag":476,"props":5049,"children":5050},{"style":483},[5051],{"type":44,"value":510},{"type":39,"tag":476,"props":5053,"children":5054},{"style":483},[5055],{"type":44,"value":4790},{"type":39,"tag":476,"props":5057,"children":5058},{"class":478,"line":795},[5059,5063,5067,5071,5075,5079,5083],{"type":39,"tag":476,"props":5060,"children":5061},{"style":483},[5062],{"type":44,"value":494},{"type":39,"tag":476,"props":5064,"children":5065},{"style":497},[5066],{"type":44,"value":4802},{"type":39,"tag":476,"props":5068,"children":5069},{"style":483},[5070],{"type":44,"value":505},{"type":39,"tag":476,"props":5072,"children":5073},{"style":483},[5074],{"type":44,"value":510},{"type":39,"tag":476,"props":5076,"children":5077},{"style":483},[5078],{"type":44,"value":515},{"type":39,"tag":476,"props":5080,"children":5081},{"style":518},[5082],{"type":44,"value":4819},{"type":39,"tag":476,"props":5084,"children":5085},{"style":483},[5086],{"type":44,"value":563},{"type":39,"tag":476,"props":5088,"children":5089},{"class":478,"line":22},[5090],{"type":39,"tag":476,"props":5091,"children":5092},{"style":483},[5093],{"type":44,"value":572},{"type":39,"tag":40,"props":5095,"children":5096},{},[5097,5099,5104],{"type":44,"value":5098},"After receiving this notification, call ",{"type":39,"tag":67,"props":5100,"children":5102},{"className":5101},[],[5103],{"type":44,"value":660},{"type":44,"value":5105}," to get the current full roster.",{"type":39,"tag":100,"props":5107,"children":5108},{},[],{"type":39,"tag":424,"props":5110,"children":5112},{"id":5111},"sub-3-deduplicate-member-added-notifications",[5113],{"type":44,"value":5114},"SUB-3 · Deduplicate member-added notifications",{"type":39,"tag":40,"props":5116,"children":5117},{},[5118],{"type":39,"tag":47,"props":5119,"children":5120},{},[5121],{"type":44,"value":2043},{"type":39,"tag":40,"props":5123,"children":5124},{},[5125,5127,5133,5135,5140,5142,5147],{"type":44,"value":5126},"A user who is both a direct and indirect member of a shared channel may trigger duplicate ",{"type":39,"tag":67,"props":5128,"children":5130},{"className":5129},[],[5131],{"type":44,"value":5132},"memberAdded",{"type":44,"value":5134}," notifications. Before processing, check whether the user already exists in your roster using ",{"type":39,"tag":67,"props":5136,"children":5138},{"className":5137},[],[5139],{"type":44,"value":660},{"type":44,"value":5141}," and deduplicate using the ",{"type":39,"tag":67,"props":5143,"children":5145},{"className":5144},[],[5146],{"type":44,"value":1225},{"type":44,"value":5148}," annotation to distinguish the source.",{"type":39,"tag":100,"props":5150,"children":5151},{},[],{"type":39,"tag":424,"props":5153,"children":5155},{"id":5154},"sub-4-include-lifecyclenotificationurl-for-subscriptions-with-expiry-1-hour",[5156,5158,5164],{"type":44,"value":5157},"SUB-4 · Include ",{"type":39,"tag":67,"props":5159,"children":5161},{"className":5160},[],[5162],{"type":44,"value":5163},"lifecycleNotificationUrl",{"type":44,"value":5165}," for subscriptions with expiry > 1 hour",{"type":39,"tag":40,"props":5167,"children":5168},{},[5169],{"type":39,"tag":47,"props":5170,"children":5171},{},[5172],{"type":44,"value":625},{"type":39,"tag":40,"props":5174,"children":5175},{},[5176,5178,5183,5185,5190,5192,5197],{"type":44,"value":5177},"If the ",{"type":39,"tag":67,"props":5179,"children":5181},{"className":5180},[],[5182],{"type":44,"value":4802},{"type":44,"value":5184}," in a subscription request is more than ",{"type":39,"tag":47,"props":5186,"children":5187},{},[5188],{"type":44,"value":5189},"one hour",{"type":44,"value":5191}," in the future, you must include a ",{"type":39,"tag":67,"props":5193,"children":5195},{"className":5194},[],[5196],{"type":44,"value":5163},{"type":44,"value":5198}," property. Without it, subscription creation fails with an error.",{"type":39,"tag":465,"props":5200,"children":5202},{"className":467,"code":5201,"language":469,"meta":470,"style":470},"{\n  \"changeType\": \"created,deleted,updated\",\n  \"notificationUrl\": \"https:\u002F\u002Fyour-webhook\u002Fapi\u002Fnotifications\",\n  \"lifecycleNotificationUrl\": \"https:\u002F\u002Fyour-webhook\u002Fapi\u002Flifecycle\",\n  \"resource\": \"\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers?notifyOnIndirectMembershipUpdate=true\",\n  \"expirationDateTime\": \"2024-09-19T11:00:00.0000000Z\"\n}\n",[5203],{"type":39,"tag":67,"props":5204,"children":5205},{"__ignoreMap":470},[5206,5213,5248,5283,5319,5355,5386],{"type":39,"tag":476,"props":5207,"children":5208},{"class":478,"line":479},[5209],{"type":39,"tag":476,"props":5210,"children":5211},{"style":483},[5212],{"type":44,"value":486},{"type":39,"tag":476,"props":5214,"children":5215},{"class":478,"line":26},[5216,5220,5224,5228,5232,5236,5240,5244],{"type":39,"tag":476,"props":5217,"children":5218},{"style":483},[5219],{"type":44,"value":494},{"type":39,"tag":476,"props":5221,"children":5222},{"style":497},[5223],{"type":44,"value":4666},{"type":39,"tag":476,"props":5225,"children":5226},{"style":483},[5227],{"type":44,"value":505},{"type":39,"tag":476,"props":5229,"children":5230},{"style":483},[5231],{"type":44,"value":510},{"type":39,"tag":476,"props":5233,"children":5234},{"style":483},[5235],{"type":44,"value":515},{"type":39,"tag":476,"props":5237,"children":5238},{"style":518},[5239],{"type":44,"value":4683},{"type":39,"tag":476,"props":5241,"children":5242},{"style":483},[5243],{"type":44,"value":505},{"type":39,"tag":476,"props":5245,"children":5246},{"style":483},[5247],{"type":44,"value":530},{"type":39,"tag":476,"props":5249,"children":5250},{"class":478,"line":533},[5251,5255,5259,5263,5267,5271,5275,5279],{"type":39,"tag":476,"props":5252,"children":5253},{"style":483},[5254],{"type":44,"value":494},{"type":39,"tag":476,"props":5256,"children":5257},{"style":497},[5258],{"type":44,"value":4703},{"type":39,"tag":476,"props":5260,"children":5261},{"style":483},[5262],{"type":44,"value":505},{"type":39,"tag":476,"props":5264,"children":5265},{"style":483},[5266],{"type":44,"value":510},{"type":39,"tag":476,"props":5268,"children":5269},{"style":483},[5270],{"type":44,"value":515},{"type":39,"tag":476,"props":5272,"children":5273},{"style":518},[5274],{"type":44,"value":4720},{"type":39,"tag":476,"props":5276,"children":5277},{"style":483},[5278],{"type":44,"value":505},{"type":39,"tag":476,"props":5280,"children":5281},{"style":483},[5282],{"type":44,"value":530},{"type":39,"tag":476,"props":5284,"children":5285},{"class":478,"line":566},[5286,5290,5294,5298,5302,5306,5311,5315],{"type":39,"tag":476,"props":5287,"children":5288},{"style":483},[5289],{"type":44,"value":494},{"type":39,"tag":476,"props":5291,"children":5292},{"style":497},[5293],{"type":44,"value":5163},{"type":39,"tag":476,"props":5295,"children":5296},{"style":483},[5297],{"type":44,"value":505},{"type":39,"tag":476,"props":5299,"children":5300},{"style":483},[5301],{"type":44,"value":510},{"type":39,"tag":476,"props":5303,"children":5304},{"style":483},[5305],{"type":44,"value":515},{"type":39,"tag":476,"props":5307,"children":5308},{"style":518},[5309],{"type":44,"value":5310},"https:\u002F\u002Fyour-webhook\u002Fapi\u002Flifecycle",{"type":39,"tag":476,"props":5312,"children":5313},{"style":483},[5314],{"type":44,"value":505},{"type":39,"tag":476,"props":5316,"children":5317},{"style":483},[5318],{"type":44,"value":530},{"type":39,"tag":476,"props":5320,"children":5321},{"class":478,"line":786},[5322,5326,5330,5334,5338,5342,5347,5351],{"type":39,"tag":476,"props":5323,"children":5324},{"style":483},[5325],{"type":44,"value":494},{"type":39,"tag":476,"props":5327,"children":5328},{"style":497},[5329],{"type":44,"value":4740},{"type":39,"tag":476,"props":5331,"children":5332},{"style":483},[5333],{"type":44,"value":505},{"type":39,"tag":476,"props":5335,"children":5336},{"style":483},[5337],{"type":44,"value":510},{"type":39,"tag":476,"props":5339,"children":5340},{"style":483},[5341],{"type":44,"value":515},{"type":39,"tag":476,"props":5343,"children":5344},{"style":518},[5345],{"type":44,"value":5346},"\u002Fteams\u002F{team-id}\u002Fchannels\u002FgetAllMembers?notifyOnIndirectMembershipUpdate=true",{"type":39,"tag":476,"props":5348,"children":5349},{"style":483},[5350],{"type":44,"value":505},{"type":39,"tag":476,"props":5352,"children":5353},{"style":483},[5354],{"type":44,"value":530},{"type":39,"tag":476,"props":5356,"children":5357},{"class":478,"line":795},[5358,5362,5366,5370,5374,5378,5382],{"type":39,"tag":476,"props":5359,"children":5360},{"style":483},[5361],{"type":44,"value":494},{"type":39,"tag":476,"props":5363,"children":5364},{"style":497},[5365],{"type":44,"value":4802},{"type":39,"tag":476,"props":5367,"children":5368},{"style":483},[5369],{"type":44,"value":505},{"type":39,"tag":476,"props":5371,"children":5372},{"style":483},[5373],{"type":44,"value":510},{"type":39,"tag":476,"props":5375,"children":5376},{"style":483},[5377],{"type":44,"value":515},{"type":39,"tag":476,"props":5379,"children":5380},{"style":518},[5381],{"type":44,"value":4819},{"type":39,"tag":476,"props":5383,"children":5384},{"style":483},[5385],{"type":44,"value":563},{"type":39,"tag":476,"props":5387,"children":5388},{"class":478,"line":22},[5389],{"type":39,"tag":476,"props":5390,"children":5391},{"style":483},[5392],{"type":44,"value":572},{"type":39,"tag":40,"props":5394,"children":5395},{},[5396,5400],{"type":39,"tag":47,"props":5397,"children":5398},{},[5399],{"type":44,"value":580},{"type":44,"value":5401}," Subscription creation requests with expiry > 1 hour will be rejected by the platform.",{"type":39,"tag":100,"props":5403,"children":5404},{},[],{"type":39,"tag":55,"props":5406,"children":5408},{"id":5407},"ctx-context-api-rules",[5409],{"type":44,"value":5410},"CTX — Context API Rules",{"type":39,"tag":424,"props":5412,"children":5414},{"id":5413},"ctx-1-use-hostteamgroupid-and-hosttenantid-from-getcontext-for-graph-calls",[5415,5417,5422,5423,5428,5429,5434],{"type":44,"value":5416},"CTX-1 · Use ",{"type":39,"tag":67,"props":5418,"children":5420},{"className":5419},[],[5421],{"type":44,"value":1551},{"type":44,"value":2005},{"type":39,"tag":67,"props":5424,"children":5426},{"className":5425},[],[5427],{"type":44,"value":1618},{"type":44,"value":1632},{"type":39,"tag":67,"props":5430,"children":5432},{"className":5431},[],[5433],{"type":44,"value":1587},{"type":44,"value":5435}," for Graph calls",{"type":39,"tag":40,"props":5437,"children":5438},{},[5439],{"type":39,"tag":47,"props":5440,"children":5441},{},[5442],{"type":44,"value":625},{"type":39,"tag":40,"props":5444,"children":5445},{},[5446,5448,5453],{"type":44,"value":5447},"For shared and private channels, ",{"type":39,"tag":67,"props":5449,"children":5451},{"className":5450},[],[5452],{"type":44,"value":1587},{"type":44,"value":5454}," returns two new properties:",{"type":39,"tag":76,"props":5456,"children":5457},{},[5458,5468],{"type":39,"tag":80,"props":5459,"children":5460},{},[5461,5466],{"type":39,"tag":67,"props":5462,"children":5464},{"className":5463},[],[5465],{"type":44,"value":1551},{"type":44,"value":5467}," — the group ID of the team that hosts the channel",{"type":39,"tag":80,"props":5469,"children":5470},{},[5471,5476],{"type":39,"tag":67,"props":5472,"children":5474},{"className":5473},[],[5475],{"type":44,"value":1618},{"type":44,"value":1620},{"type":39,"tag":40,"props":5478,"children":5479},{},[5480,5482,5487],{"type":44,"value":5481},"Use these (not the bare ",{"type":39,"tag":67,"props":5483,"children":5485},{"className":5484},[],[5486],{"type":44,"value":1559},{"type":44,"value":5488},") when calling Graph APIs for channel membership, file access, or tenant comparison.",{"type":39,"tag":111,"props":5490,"children":5491},{},[5492],{"type":39,"tag":40,"props":5493,"children":5494},{},[5495,5497,5503,5505,5510,5512,5518,5520,5526],{"type":44,"value":5496},"See also ",{"type":39,"tag":67,"props":5498,"children":5500},{"className":5499},[],[5501],{"type":44,"value":5502},"MEM-5",{"type":44,"value":5504}," for the specific membership API implication: ",{"type":39,"tag":67,"props":5506,"children":5508},{"className":5507},[],[5509],{"type":44,"value":1551},{"type":44,"value":5511}," must be used as the ",{"type":39,"tag":67,"props":5513,"children":5515},{"className":5514},[],[5516],{"type":44,"value":5517},"{team-id}",{"type":44,"value":5519}," parameter in all ",{"type":39,"tag":67,"props":5521,"children":5523},{"className":5522},[],[5524],{"type":44,"value":5525},"\u002Fteams\u002F{team-id}\u002Fchannels\u002F{channel-id}\u002F...",{"type":44,"value":5527}," Graph API calls.",{"type":39,"tag":100,"props":5529,"children":5530},{},[],{"type":39,"tag":424,"props":5532,"children":5534},{"id":5533},"ctx-2-use-channelownertenantid-for-cross-tenant-sharepoint-access",[5535,5537,5542],{"type":44,"value":5536},"CTX-2 · Use ",{"type":39,"tag":67,"props":5538,"children":5540},{"className":5539},[],[5541],{"type":44,"value":3651},{"type":44,"value":5543}," for cross-tenant SharePoint access",{"type":39,"tag":40,"props":5545,"children":5546},{},[5547],{"type":39,"tag":47,"props":5548,"children":5549},{},[5550],{"type":44,"value":625},{"type":39,"tag":40,"props":5552,"children":5553},{},[5554,5556,5561,5563,5569,5570,5575],{"type":44,"value":5555},"When calling ",{"type":39,"tag":67,"props":5557,"children":5559},{"className":5558},[],[5560],{"type":44,"value":3967},{"type":44,"value":5562}," for SharePoint resources from a tab, use ",{"type":39,"tag":67,"props":5564,"children":5566},{"className":5565},[],[5567],{"type":44,"value":5568},"context.channel.ownerTenantId",{"type":44,"value":1653},{"type":39,"tag":67,"props":5571,"children":5573},{"className":5572},[],[5574],{"type":44,"value":2262},{"type":44,"value":5576}," parameter. This ensures tokens are scoped to the host tenant where the SharePoint site lives.",{"type":39,"tag":100,"props":5578,"children":5579},{},[],{"type":39,"tag":104,"props":5581,"children":5583},{"id":5582},"scanning-instructions",[5584],{"type":44,"value":5585},"Scanning Instructions",{"type":39,"tag":55,"props":5587,"children":5589},{"id":5588},"step-1-short-circuit-check-if-the-app-has-channel-scope",[5590],{"type":44,"value":5591},"Step 1 — Short Circuit: Check if the app has channel scope",{"type":39,"tag":40,"props":5593,"children":5594},{},[5595,5600,5602,5608,5610,5616],{"type":39,"tag":47,"props":5596,"children":5597},{},[5598],{"type":44,"value":5599},"Before doing any deep scan",{"type":44,"value":5601},", locate ",{"type":39,"tag":67,"props":5603,"children":5605},{"className":5604},[],[5606],{"type":44,"value":5607},"manifest.json",{"type":44,"value":5609}," (or files under ",{"type":39,"tag":67,"props":5611,"children":5613},{"className":5612},[],[5614],{"type":44,"value":5615},"appPackage\u002F",{"type":44,"value":5617},").",{"type":39,"tag":40,"props":5619,"children":5620},{},[5621,5623,5628,5630,5636,5638,5644,5646,5652,5653,5659,5660,5666,5667,5673],{"type":44,"value":5622},"Check whether ",{"type":39,"tag":47,"props":5624,"children":5625},{},[5626],{"type":44,"value":5627},"any",{"type":44,"value":5629}," element in the manifest has a ",{"type":39,"tag":67,"props":5631,"children":5633},{"className":5632},[],[5634],{"type":44,"value":5635},"\"scopes\"",{"type":44,"value":5637}," array that contains ",{"type":39,"tag":67,"props":5639,"children":5641},{"className":5640},[],[5642],{"type":44,"value":5643},"\"team\"",{"type":44,"value":5645},". This includes ",{"type":39,"tag":67,"props":5647,"children":5649},{"className":5648},[],[5650],{"type":44,"value":5651},"configurableTabs",{"type":44,"value":662},{"type":39,"tag":67,"props":5654,"children":5656},{"className":5655},[],[5657],{"type":44,"value":5658},"staticTabs",{"type":44,"value":662},{"type":39,"tag":67,"props":5661,"children":5663},{"className":5662},[],[5664],{"type":44,"value":5665},"bots",{"type":44,"value":662},{"type":39,"tag":67,"props":5668,"children":5670},{"className":5669},[],[5671],{"type":44,"value":5672},"composeExtensions",{"type":44,"value":5674},", etc.",{"type":39,"tag":76,"props":5676,"children":5677},{},[5678,5716,5738],{"type":39,"tag":80,"props":5679,"children":5680},{},[5681,5700,5702,5707,5709,5714],{"type":39,"tag":47,"props":5682,"children":5683},{},[5684,5686,5691,5693,5699],{"type":44,"value":5685},"If NO element has ",{"type":39,"tag":67,"props":5687,"children":5689},{"className":5688},[],[5690],{"type":44,"value":5643},{"type":44,"value":5692}," in its ",{"type":39,"tag":67,"props":5694,"children":5696},{"className":5695},[],[5697],{"type":44,"value":5698},"scopes",{"type":44,"value":510},{"type":44,"value":5701}," The app has no channel installation surface (personal-only or groupChat-only). NGC rules do not apply. ",{"type":39,"tag":47,"props":5703,"children":5704},{},[5705],{"type":44,"value":5706},"Stop here",{"type":44,"value":5708}," and report: ✅ ",{"type":39,"tag":47,"props":5710,"children":5711},{},[5712],{"type":44,"value":5713},"Channel Ready — NGC Not Applicable",{"type":44,"value":5715}," with a note that the app has no team\u002Fchannel scope and therefore does not need NGC compliance.",{"type":39,"tag":80,"props":5717,"children":5718},{},[5719,5736],{"type":39,"tag":47,"props":5720,"children":5721},{},[5722,5724,5729,5730,5735],{"type":44,"value":5723},"If ANY element has ",{"type":39,"tag":67,"props":5725,"children":5727},{"className":5726},[],[5728],{"type":44,"value":5643},{"type":44,"value":5692},{"type":39,"tag":67,"props":5731,"children":5733},{"className":5732},[],[5734],{"type":44,"value":5698},{"type":44,"value":510},{"type":44,"value":5737}," The app can be installed in channels. Continue to Step 2.",{"type":39,"tag":80,"props":5739,"children":5740},{},[5741,5752],{"type":39,"tag":47,"props":5742,"children":5743},{},[5744,5745,5750],{"type":44,"value":1968},{"type":39,"tag":67,"props":5746,"children":5748},{"className":5747},[],[5749],{"type":44,"value":5607},{"type":44,"value":5751}," is not found:",{"type":44,"value":5753}," Issue a warning that manifest is not available (may be hosted on CDN). Proceed with code scanning and note that manifest must be verified separately.",{"type":39,"tag":55,"props":5755,"children":5757},{"id":5756},"step-2-manifest-check-2-mandatory-items-deferred-rsc-check",[5758],{"type":44,"value":5759},"Step 2 — Manifest Check (2 mandatory items + deferred RSC check)",{"type":39,"tag":40,"props":5761,"children":5762},{},[5763,5765,5770,5772,5777],{"type":44,"value":5764},"Read ",{"type":39,"tag":67,"props":5766,"children":5768},{"className":5767},[],[5769],{"type":44,"value":5607},{"type":44,"value":5771}," and check ",{"type":39,"tag":47,"props":5773,"children":5774},{},[5775],{"type":44,"value":5776},"only",{"type":44,"value":5778}," the items below. Do not evaluate any other manifest section. Ignore all URLs in the manifest.",{"type":39,"tag":5780,"props":5781,"children":5782},"ol",{},[5783,5811],{"type":39,"tag":80,"props":5784,"children":5785},{},[5786,5794,5796,5802,5804,5809],{"type":39,"tag":47,"props":5787,"children":5788},{},[5789],{"type":39,"tag":67,"props":5790,"children":5792},{"className":5791},[],[5793],{"type":44,"value":500},{"type":44,"value":5795}," — must be ",{"type":39,"tag":67,"props":5797,"children":5799},{"className":5798},[],[5800],{"type":44,"value":5801},"\"1.25\"",{"type":44,"value":5803}," or higher. Flag as ",{"type":39,"tag":47,"props":5805,"children":5806},{},[5807],{"type":44,"value":5808},"CRITICAL",{"type":44,"value":5810}," if lower or missing.",{"type":39,"tag":80,"props":5812,"children":5813},{},[5814,5822,5824,5830,5832,5836],{"type":39,"tag":47,"props":5815,"children":5816},{},[5817],{"type":39,"tag":67,"props":5818,"children":5820},{"className":5819},[],[5821],{"type":44,"value":435},{"type":44,"value":5823}," — must be present with value ",{"type":39,"tag":67,"props":5825,"children":5827},{"className":5826},[],[5828],{"type":44,"value":5829},"\"tier1\"",{"type":44,"value":5831},". Flag as ",{"type":39,"tag":47,"props":5833,"children":5834},{},[5835],{"type":44,"value":5808},{"type":44,"value":5837}," if missing or wrong value.",{"type":39,"tag":40,"props":5839,"children":5840},{},[5841,5859],{"type":39,"tag":47,"props":5842,"children":5843},{},[5844,5846,5851,5852,5857],{"type":44,"value":5845},"RSC permissions (",{"type":39,"tag":67,"props":5847,"children":5849},{"className":5848},[],[5850],{"type":44,"value":652},{"type":44,"value":662},{"type":39,"tag":67,"props":5853,"children":5855},{"className":5854},[],[5856],{"type":44,"value":682},{"type":44,"value":5858},") are NOT checked here.",{"type":44,"value":5860}," They are conditional on what the code actually does and are evaluated during the code scan in Step 3:",{"type":39,"tag":76,"props":5862,"children":5863},{},[5864,5881],{"type":39,"tag":80,"props":5865,"children":5866},{},[5867,5872,5874,5879],{"type":39,"tag":67,"props":5868,"children":5870},{"className":5869},[],[5871],{"type":44,"value":652},{"type":44,"value":5873}," is checked inside the ",{"type":39,"tag":47,"props":5875,"children":5876},{},[5877],{"type":44,"value":5878},"Membership APIs",{"type":44,"value":5880}," scan section.",{"type":39,"tag":80,"props":5882,"children":5883},{},[5884,5889,5890,5894],{"type":39,"tag":67,"props":5885,"children":5887},{"className":5886},[],[5888],{"type":44,"value":682},{"type":44,"value":5873},{"type":39,"tag":47,"props":5891,"children":5892},{},[5893],{"type":44,"value":298},{"type":44,"value":5880},{"type":39,"tag":40,"props":5896,"children":5897},{},[5898],{"type":44,"value":5899},"Do not flag RSC permissions as violations in this step.",{"type":39,"tag":40,"props":5901,"children":5902},{},[5903],{"type":44,"value":5904},"Do not flag any other manifest fields as violations.",{"type":39,"tag":55,"props":5906,"children":5908},{"id":5907},"step-3-code-scan",[5909],{"type":44,"value":5910},"Step 3 — Code Scan",{"type":39,"tag":40,"props":5912,"children":5913},{},[5914],{"type":44,"value":5915},"Using the rules above as evaluation criteria, systematically search the codebase for evidence of compliance or violation. Use targeted searches across all relevant file types.",{"type":39,"tag":111,"props":5917,"children":5918},{},[5919],{"type":39,"tag":40,"props":5920,"children":5921},{},[5922,5927,5929,5934,5936,5941],{"type":39,"tag":47,"props":5923,"children":5924},{},[5925],{"type":44,"value":5926},"Construct applicability principle:",{"type":44,"value":5928}," Every rule category below is conditional on the app actually using that construct. Before scanning a category, first confirm the construct is present. If it is absent, ",{"type":39,"tag":47,"props":5930,"children":5931},{},[5932],{"type":44,"value":5933},"skip the entire category",{"type":44,"value":5935}," and note it as \"not applicable\" in the report — do not flag phantom violations for code that doesn't exist. The applicability check for each category is listed under its ",{"type":39,"tag":47,"props":5937,"children":5938},{},[5939],{"type":44,"value":5940},"\"Skip if\"",{"type":44,"value":5942}," line.",{"type":39,"tag":100,"props":5944,"children":5945},{},[],{"type":39,"tag":424,"props":5947,"children":5949},{"id":5948},"membership-apis",[5950],{"type":44,"value":5878},{"type":39,"tag":40,"props":5952,"children":5953},{},[5954,5959,5961,5967,5968,5974,5975,5981,5982,5987],{"type":39,"tag":47,"props":5955,"children":5956},{},[5957],{"type":44,"value":5958},"Skip if:",{"type":44,"value":5960}," No Graph API calls to teams, channels, groups, or membership endpoints exist anywhere in the codebase (search for ",{"type":39,"tag":67,"props":5962,"children":5964},{"className":5963},[],[5965],{"type":44,"value":5966},"graph",{"type":44,"value":662},{"type":39,"tag":67,"props":5969,"children":5971},{"className":5970},[],[5972],{"type":44,"value":5973},"teams",{"type":44,"value":662},{"type":39,"tag":67,"props":5976,"children":5978},{"className":5977},[],[5979],{"type":44,"value":5980},"channels",{"type":44,"value":662},{"type":39,"tag":67,"props":5983,"children":5985},{"className":5984},[],[5986],{"type":44,"value":668},{"type":44,"value":5988},"). If absent, skip all MEM rules.",{"type":39,"tag":40,"props":5990,"children":5991},{},[5992],{"type":39,"tag":47,"props":5993,"children":5994},{},[5995],{"type":44,"value":5996},"Flag as violations:",{"type":39,"tag":76,"props":5998,"children":5999},{},[6000,6018,6029,6040,6051,6061],{"type":39,"tag":80,"props":6001,"children":6002},{},[6003,6009,6010,6016],{"type":39,"tag":67,"props":6004,"children":6006},{"className":6005},[],[6007],{"type":44,"value":6008},"GetGroupMembersAsync",{"type":44,"value":3606},{"type":39,"tag":67,"props":6011,"children":6013},{"className":6012},[],[6014],{"type":44,"value":6015},"\u002Fgroups\u002F{id}\u002Fmembers",{"type":44,"value":6017}," used for channel access decisions",{"type":39,"tag":80,"props":6019,"children":6020},{},[6021,6027],{"type":39,"tag":67,"props":6022,"children":6024},{"className":6023},[],[6025],{"type":44,"value":6026},".Members.GetAsync()",{"type":44,"value":6028}," — C# Graph SDK, returns direct only",{"type":39,"tag":80,"props":6030,"children":6031},{},[6032,6038],{"type":39,"tag":67,"props":6033,"children":6035},{"className":6034},[],[6036],{"type":44,"value":6037},".members().get()",{"type":44,"value":6039}," — Java Graph SDK, returns direct only",{"type":39,"tag":80,"props":6041,"children":6042},{},[6043,6049],{"type":39,"tag":67,"props":6044,"children":6046},{"className":6045},[],[6047],{"type":44,"value":6048},".members.get()",{"type":44,"value":6050}," — Python Graph SDK, returns direct only",{"type":39,"tag":80,"props":6052,"children":6053},{},[6054,6059],{"type":39,"tag":67,"props":6055,"children":6057},{"className":6056},[],[6058],{"type":44,"value":1321},{"type":44,"value":6060}," — JavaScript, returns direct only",{"type":39,"tag":80,"props":6062,"children":6063},{},[6064,6070],{"type":39,"tag":67,"props":6065,"children":6067},{"className":6066},[],[6068],{"type":44,"value":6069},"\u002Fteams\u002F{id}\u002Fmembers",{"type":44,"value":6071}," — team-level member list, not channel-scoped",{"type":39,"tag":40,"props":6073,"children":6074},{},[6075],{"type":39,"tag":47,"props":6076,"children":6077},{},[6078],{"type":44,"value":6079},"Look for correct usage:",{"type":39,"tag":76,"props":6081,"children":6082},{},[6083,6094,6105,6116,6126],{"type":39,"tag":80,"props":6084,"children":6085},{},[6086,6092],{"type":39,"tag":67,"props":6087,"children":6089},{"className":6088},[],[6090],{"type":44,"value":6091},".AllMembers.GetAsync()",{"type":44,"value":6093}," — C# Graph SDK ✅",{"type":39,"tag":80,"props":6095,"children":6096},{},[6097,6103],{"type":39,"tag":67,"props":6098,"children":6100},{"className":6099},[],[6101],{"type":44,"value":6102},".allMembers().get()",{"type":44,"value":6104}," — Java Graph SDK ✅",{"type":39,"tag":80,"props":6106,"children":6107},{},[6108,6114],{"type":39,"tag":67,"props":6109,"children":6111},{"className":6110},[],[6112],{"type":44,"value":6113},".all_members.get()",{"type":44,"value":6115}," — Python Graph SDK ✅",{"type":39,"tag":80,"props":6117,"children":6118},{},[6119,6124],{"type":39,"tag":67,"props":6120,"children":6122},{"className":6121},[],[6123],{"type":44,"value":1330},{"type":44,"value":6125}," — JavaScript ✅",{"type":39,"tag":80,"props":6127,"children":6128},{},[6129,6135],{"type":39,"tag":67,"props":6130,"children":6132},{"className":6131},[],[6133],{"type":44,"value":6134},"\u002Fteams\u002F{id}\u002Fchannels\u002F{id}\u002FallMembers",{"type":44,"value":6136}," in raw HTTP calls ✅",{"type":39,"tag":40,"props":6138,"children":6139},{},[6140],{"type":39,"tag":47,"props":6141,"children":6142},{},[6143],{"type":44,"value":6144},"Also search for:",{"type":39,"tag":76,"props":6146,"children":6147},{},[6148,6158,6168,6192],{"type":39,"tag":80,"props":6149,"children":6150},{},[6151,6156],{"type":39,"tag":67,"props":6152,"children":6154},{"className":6153},[],[6155],{"type":44,"value":1449},{"type":44,"value":6157}," — note if used or absent on member-removed events",{"type":39,"tag":80,"props":6159,"children":6160},{},[6161,6166],{"type":39,"tag":67,"props":6162,"children":6164},{"className":6163},[],[6165],{"type":44,"value":1225},{"type":44,"value":6167}," — note if absent (Bucket 3 item)",{"type":39,"tag":80,"props":6169,"children":6170},{},[6171,6176,6178,6183,6185,6190],{"type":39,"tag":67,"props":6172,"children":6174},{"className":6173},[],[6175],{"type":44,"value":1559},{"type":44,"value":6177}," used as ",{"type":39,"tag":67,"props":6179,"children":6181},{"className":6180},[],[6182],{"type":44,"value":1567},{"type":44,"value":6184}," in Graph calls — flag if ",{"type":39,"tag":67,"props":6186,"children":6188},{"className":6187},[],[6189],{"type":44,"value":1551},{"type":44,"value":6191}," is not used instead",{"type":39,"tag":80,"props":6193,"children":6194},{},[6195,6200],{"type":39,"tag":67,"props":6196,"children":6198},{"className":6197},[],[6199],{"type":44,"value":1551},{"type":44,"value":6201}," — confirm it's read from context and passed to Graph APIs",{"type":39,"tag":40,"props":6203,"children":6204},{},[6205,6210,6212,6217,6218,6223],{"type":39,"tag":47,"props":6206,"children":6207},{},[6208],{"type":44,"value":6209},"RSC manifest check (MAN-2 — only if membership APIs are in use):",{"type":44,"value":6211},"\nIf the app calls ",{"type":39,"tag":67,"props":6213,"children":6215},{"className":6214},[],[6216],{"type":44,"value":660},{"type":44,"value":662},{"type":39,"tag":67,"props":6219,"children":6221},{"className":6220},[],[6222],{"type":44,"value":668},{"type":44,"value":6224},", or any channel membership Graph API, check whether the app uses RSC (Resource-Specific Consent) or service-level auth (client credentials \u002F OBO with delegated Graph permissions):",{"type":39,"tag":76,"props":6226,"children":6227},{},[6228,6262,6273],{"type":39,"tag":80,"props":6229,"children":6230},{},[6231,6233,6238,6240,6245,6247,6252,6254,6260],{"type":44,"value":6232},"If using ",{"type":39,"tag":47,"props":6234,"children":6235},{},[6236],{"type":44,"value":6237},"RSC",{"type":44,"value":6239},": flag as ",{"type":39,"tag":47,"props":6241,"children":6242},{},[6243],{"type":44,"value":6244},"HIGH",{"type":44,"value":6246}," if ",{"type":39,"tag":67,"props":6248,"children":6250},{"className":6249},[],[6251],{"type":44,"value":652},{"type":44,"value":6253}," is absent from ",{"type":39,"tag":67,"props":6255,"children":6257},{"className":6256},[],[6258],{"type":44,"value":6259},"authorization.permissions.resourceSpecific",{"type":44,"value":6261}," in the manifest.",{"type":39,"tag":80,"props":6263,"children":6264},{},[6265,6266,6271],{"type":44,"value":6232},{"type":39,"tag":47,"props":6267,"children":6268},{},[6269],{"type":44,"value":6270},"service-level auth",{"type":44,"value":6272},": no manifest RSC entry is needed — do not flag.",{"type":39,"tag":80,"props":6274,"children":6275},{},[6276],{"type":44,"value":6277},"If auth method is unclear: note it as a Bucket 2 observation.",{"type":39,"tag":100,"props":6279,"children":6280},{},[],{"type":39,"tag":424,"props":6282,"children":6284},{"id":6283},"file-sharepoint",[6285],{"type":44,"value":298},{"type":39,"tag":40,"props":6287,"children":6288},{},[6289,6293,6295,6301,6302,6308,6309,6314,6315,6321,6322,6327],{"type":39,"tag":47,"props":6290,"children":6291},{},[6292],{"type":44,"value":5958},{"type":44,"value":6294}," No SharePoint, OneDrive, or drive-related code exists (search for ",{"type":39,"tag":67,"props":6296,"children":6298},{"className":6297},[],[6299],{"type":44,"value":6300},"drive",{"type":44,"value":662},{"type":39,"tag":67,"props":6303,"children":6305},{"className":6304},[],[6306],{"type":44,"value":6307},"sharepoint",{"type":44,"value":662},{"type":39,"tag":67,"props":6310,"children":6312},{"className":6311},[],[6313],{"type":44,"value":689},{"type":44,"value":662},{"type":39,"tag":67,"props":6316,"children":6318},{"className":6317},[],[6319],{"type":44,"value":6320},"Shared Documents",{"type":44,"value":662},{"type":39,"tag":67,"props":6323,"children":6325},{"className":6324},[],[6326],{"type":44,"value":2350},{"type":44,"value":6328},"). If absent, skip all FS rules.",{"type":39,"tag":40,"props":6330,"children":6331},{},[6332],{"type":39,"tag":47,"props":6333,"children":6334},{},[6335],{"type":44,"value":5996},{"type":39,"tag":76,"props":6337,"children":6338},{},[6339,6352,6370],{"type":39,"tag":80,"props":6340,"children":6341},{},[6342,6344,6350],{"type":44,"value":6343},"Hardcoded ",{"type":39,"tag":67,"props":6345,"children":6347},{"className":6346},[],[6348],{"type":44,"value":6349},"\u002FShared Documents\u002F",{"type":44,"value":6351}," paths in SPO URLs",{"type":39,"tag":80,"props":6353,"children":6354},{},[6355,6360,6362,6368],{"type":39,"tag":67,"props":6356,"children":6358},{"className":6357},[],[6359],{"type":44,"value":2350},{"type":44,"value":6361}," cached at team level without ",{"type":39,"tag":67,"props":6363,"children":6365},{"className":6364},[],[6366],{"type":44,"value":6367},"channelId",{"type":44,"value":6369}," as key",{"type":39,"tag":80,"props":6371,"children":6372},{},[6373],{"type":44,"value":6374},"Org-wide or anonymous link generation patterns",{"type":39,"tag":40,"props":6376,"children":6377},{},[6378],{"type":39,"tag":47,"props":6379,"children":6380},{},[6381],{"type":44,"value":6079},{"type":39,"tag":76,"props":6383,"children":6384},{},[6385,6395,6406,6416],{"type":39,"tag":80,"props":6386,"children":6387},{},[6388,6393],{"type":39,"tag":67,"props":6389,"children":6391},{"className":6390},[],[6392],{"type":44,"value":689},{"type":44,"value":6394}," in API calls — confirms FS-2 compliance ✅",{"type":39,"tag":80,"props":6396,"children":6397},{},[6398,6404],{"type":39,"tag":67,"props":6399,"children":6401},{"className":6400},[],[6402],{"type":44,"value":6403},".FilesFolder.GetAsync()",{"type":44,"value":6405}," — C# SDK ✅",{"type":39,"tag":80,"props":6407,"children":6408},{},[6409,6414],{"type":39,"tag":67,"props":6410,"children":6412},{"className":6411},[],[6413],{"type":44,"value":2448},{"type":44,"value":6415}," stored per-channel ✅",{"type":39,"tag":80,"props":6417,"children":6418},{},[6419,6425],{"type":39,"tag":67,"props":6420,"children":6422},{"className":6421},[],[6423],{"type":44,"value":6424},"\u002Fdrives\u002F{driveId}\u002Fitems\u002F{itemId}\u002Finvite",{"type":44,"value":6426}," — membership-based sharing ✅",{"type":39,"tag":40,"props":6428,"children":6429},{},[6430,6435,6436,6441],{"type":39,"tag":47,"props":6431,"children":6432},{},[6433],{"type":44,"value":6434},"RSC manifest check (MAN-2 \u002F FS-5 — only if file APIs are in use):",{"type":44,"value":6211},{"type":39,"tag":67,"props":6437,"children":6439},{"className":6438},[],[6440],{"type":44,"value":689},{"type":44,"value":6442}," or accesses channel files via Graph API, check whether it uses RSC or service-level auth:",{"type":39,"tag":76,"props":6444,"children":6445},{},[6446,6472,6481],{"type":39,"tag":80,"props":6447,"children":6448},{},[6449,6450,6454,6455,6459,6460,6465,6466,6471],{"type":44,"value":6232},{"type":39,"tag":47,"props":6451,"children":6452},{},[6453],{"type":44,"value":6237},{"type":44,"value":6239},{"type":39,"tag":47,"props":6456,"children":6457},{},[6458],{"type":44,"value":6244},{"type":44,"value":6246},{"type":39,"tag":67,"props":6461,"children":6463},{"className":6462},[],[6464],{"type":44,"value":682},{"type":44,"value":6253},{"type":39,"tag":67,"props":6467,"children":6469},{"className":6468},[],[6470],{"type":44,"value":6259},{"type":44,"value":6261},{"type":39,"tag":80,"props":6473,"children":6474},{},[6475,6476,6480],{"type":44,"value":6232},{"type":39,"tag":47,"props":6477,"children":6478},{},[6479],{"type":44,"value":6270},{"type":44,"value":6272},{"type":39,"tag":80,"props":6482,"children":6483},{},[6484],{"type":44,"value":6277},{"type":39,"tag":100,"props":6486,"children":6487},{},[],{"type":39,"tag":424,"props":6489,"children":6491},{"id":6490},"bot-agent-sdk-events",[6492],{"type":44,"value":6493},"Bot & Agent SDK Events",{"type":39,"tag":40,"props":6495,"children":6496},{},[6497,6501,6503,6508],{"type":39,"tag":47,"props":6498,"children":6499},{},[6500],{"type":44,"value":5958},{"type":44,"value":6502}," The manifest has no ",{"type":39,"tag":67,"props":6504,"children":6506},{"className":6505},[],[6507],{"type":44,"value":5665},{"type":44,"value":6509}," element. If no bot is registered, skip all BOT rules including BOT-6.",{"type":39,"tag":40,"props":6511,"children":6512},{},[6513],{"type":39,"tag":47,"props":6514,"children":6515},{},[6516],{"type":44,"value":6517},"SDK version check (BOT-6 — applies to all bot projects):",{"type":39,"tag":76,"props":6519,"children":6520},{},[6521,6581,6615],{"type":39,"tag":80,"props":6522,"children":6523},{},[6524,6526,6531,6532,6537,6538,6543,6544,6549,6551,6556,6558,6564,6566,6572,6573,6579],{"type":44,"value":6525},"Search ",{"type":39,"tag":67,"props":6527,"children":6529},{"className":6528},[],[6530],{"type":44,"value":3681},{"type":44,"value":662},{"type":39,"tag":67,"props":6533,"children":6535},{"className":6534},[],[6536],{"type":44,"value":3695},{"type":44,"value":3564},{"type":39,"tag":67,"props":6539,"children":6541},{"className":6540},[],[6542],{"type":44,"value":3688},{"type":44,"value":4555},{"type":39,"tag":67,"props":6545,"children":6547},{"className":6546},[],[6548],{"type":44,"value":3604},{"type":44,"value":6550},". ",{"type":39,"tag":47,"props":6552,"children":6553},{},[6554],{"type":44,"value":6555},"Flag as CRITICAL",{"type":44,"value":6557}," if the resolved version is below ",{"type":39,"tag":67,"props":6559,"children":6561},{"className":6560},[],[6562],{"type":44,"value":6563},"2.0.0",{"type":44,"value":6565}," (e.g., ",{"type":39,"tag":67,"props":6567,"children":6569},{"className":6568},[],[6570],{"type":44,"value":6571},"1.*",{"type":44,"value":662},{"type":39,"tag":67,"props":6574,"children":6576},{"className":6575},[],[6577],{"type":44,"value":6578},"0.*",{"type":44,"value":6580},", or any pre-release below 2.0).",{"type":39,"tag":80,"props":6582,"children":6583},{},[6584,6585,6590,6591,6596,6597,6601,6603,6608,6609,6614],{"type":44,"value":6525},{"type":39,"tag":67,"props":6586,"children":6588},{"className":6587},[],[6589],{"type":44,"value":3758},{"type":44,"value":4555},{"type":39,"tag":67,"props":6592,"children":6594},{"className":6593},[],[6595],{"type":44,"value":3612},{"type":44,"value":6550},{"type":39,"tag":47,"props":6598,"children":6599},{},[6600],{"type":44,"value":6555},{"type":44,"value":6602}," if the version is below ",{"type":39,"tag":67,"props":6604,"children":6606},{"className":6605},[],[6607],{"type":44,"value":6563},{"type":44,"value":6565},{"type":39,"tag":67,"props":6610,"children":6612},{"className":6611},[],[6613],{"type":44,"value":3803},{"type":44,"value":5617},{"type":39,"tag":80,"props":6616,"children":6617},{},[6618],{"type":44,"value":6619},"If neither package is present as a direct dependency, note it and skip BOT-6.",{"type":39,"tag":40,"props":6621,"children":6622},{},[6623],{"type":39,"tag":47,"props":6624,"children":6625},{},[6626],{"type":44,"value":6627},"Flag as violations (only if bot does proactive messaging or has per-channel state):",{"type":39,"tag":76,"props":6629,"children":6630},{},[6631,6654,6670,6689],{"type":39,"tag":80,"props":6632,"children":6633},{},[6634,6639,6641,6646,6647,6652],{"type":39,"tag":67,"props":6635,"children":6637},{"className":6636},[],[6638],{"type":44,"value":3140},{"type":44,"value":6640}," present but no handling for ",{"type":39,"tag":67,"props":6642,"children":6644},{"className":6643},[],[6645],{"type":44,"value":3193},{"type":44,"value":3660},{"type":39,"tag":67,"props":6648,"children":6650},{"className":6649},[],[6651],{"type":44,"value":3200},{"type":44,"value":6653}," eventTypes",{"type":39,"tag":80,"props":6655,"children":6656},{},[6657,6663,6665],{"type":39,"tag":67,"props":6658,"children":6660},{"className":6659},[],[6661],{"type":44,"value":6662},"GetChannelData\u003CTeamsChannelData>()",{"type":44,"value":6664}," used alone for shared channel events without ",{"type":39,"tag":67,"props":6666,"children":6668},{"className":6667},[],[6669],{"type":44,"value":3459},{"type":39,"tag":80,"props":6671,"children":6672},{},[6673,6675,6680,6682,6687],{"type":44,"value":6674},"Bot setup logic only in ",{"type":39,"tag":67,"props":6676,"children":6678},{"className":6677},[],[6679],{"type":44,"value":3176},{"type":44,"value":6681}," with no ",{"type":39,"tag":67,"props":6683,"children":6685},{"className":6684},[],[6686],{"type":44,"value":3097},{"type":44,"value":6688}," equivalent",{"type":39,"tag":80,"props":6690,"children":6691},{},[6692],{"type":44,"value":6693},"Proactive message sending without verifying the bot is in the target channel",{"type":39,"tag":40,"props":6695,"children":6696},{},[6697,6702,6704,6709],{"type":39,"tag":47,"props":6698,"children":6699},{},[6700],{"type":44,"value":6701},"Note: Messaging-extension-only bots",{"type":44,"value":6703}," (link unfurling, search commands with no proactive messaging or per-channel state) are ",{"type":39,"tag":47,"props":6705,"children":6706},{},[6707],{"type":44,"value":6708},"not subject to BOT-1 through BOT-5",{"type":44,"value":6710}," — these rules only apply to bots that send proactive messages or maintain per-channel install state. BOT-6 (SDK version) applies to all bot projects regardless.",{"type":39,"tag":100,"props":6712,"children":6713},{},[],{"type":39,"tag":424,"props":6715,"children":6717},{"id":6716},"authentication-external-users",[6718],{"type":44,"value":6719},"Authentication & External Users",{"type":39,"tag":40,"props":6721,"children":6722},{},[6723,6727,6729,6734,6735,6741],{"type":39,"tag":47,"props":6724,"children":6725},{},[6726],{"type":44,"value":5958},{"type":44,"value":6728}," No authentication flows exist — no ",{"type":39,"tag":67,"props":6730,"children":6732},{"className":6731},[],[6733],{"type":44,"value":3967},{"type":44,"value":662},{"type":39,"tag":67,"props":6736,"children":6738},{"className":6737},[],[6739],{"type":44,"value":6740},"AcquireToken",{"type":44,"value":6742},", MSAL, OBO, or similar token-acquisition code found anywhere in the codebase. If absent, skip all AUTH rules.",{"type":39,"tag":40,"props":6744,"children":6745},{},[6746],{"type":39,"tag":47,"props":6747,"children":6748},{},[6749],{"type":44,"value":5996},{"type":39,"tag":76,"props":6751,"children":6752},{},[6753,6766],{"type":39,"tag":80,"props":6754,"children":6755},{},[6756,6758,6764],{"type":44,"value":6757},"Code that rejects users based on unrecognized\u002Fnon-matching tenant IDs (hardcoded tenant allowlists, blanket denial of ",{"type":39,"tag":67,"props":6759,"children":6761},{"className":6760},[],[6762],{"type":44,"value":6763},"IsEnterpriseGuest",{"type":44,"value":6765}," in shared channel contexts)",{"type":39,"tag":80,"props":6767,"children":6768},{},[6769,6774,6776,6781],{"type":39,"tag":67,"props":6770,"children":6772},{"className":6771},[],[6773],{"type":44,"value":3967},{"type":44,"value":6775}," called without passing ",{"type":39,"tag":67,"props":6777,"children":6779},{"className":6778},[],[6780],{"type":44,"value":2262},{"type":44,"value":6782}," when SharePoint is involved",{"type":39,"tag":40,"props":6784,"children":6785},{},[6786],{"type":39,"tag":47,"props":6787,"children":6788},{},[6789],{"type":44,"value":6790},"Note for Bucket 2 (suggestions, not violations):",{"type":39,"tag":76,"props":6792,"children":6793},{},[6794],{"type":39,"tag":80,"props":6795,"children":6796},{},[6797,6799,6804],{"type":44,"value":6798},"Apps that restrict functionality for external\u002Fguest users based on session flags rather than Graph ",{"type":39,"tag":67,"props":6800,"children":6802},{"className":6801},[],[6803],{"type":44,"value":2182},{"type":44,"value":6805}," — the app may intentionally not support external users, but it's worth suggesting they evaluate this per channel type",{"type":39,"tag":100,"props":6807,"children":6808},{},[],{"type":39,"tag":424,"props":6810,"children":6812},{"id":6811},"data-scoping",[6813],{"type":44,"value":364},{"type":39,"tag":40,"props":6815,"children":6816},{},[6817,6821,6823,6828,6829,6834,6835,6841,6842,6848],{"type":39,"tag":47,"props":6818,"children":6819},{},[6820],{"type":44,"value":5958},{"type":44,"value":6822}," No multi-channel aggregation, reporting, broadcast, or cross-channel post patterns exist, AND no ",{"type":39,"tag":67,"props":6824,"children":6826},{"className":6825},[],[6827],{"type":44,"value":4480},{"type":44,"value":3660},{"type":39,"tag":67,"props":6830,"children":6832},{"className":6831},[],[6833],{"type":44,"value":4473},{"type":44,"value":3660},{"type":39,"tag":67,"props":6836,"children":6838},{"className":6837},[],[6839],{"type":44,"value":6840},"MembershipType",{"type":44,"value":3660},{"type":39,"tag":67,"props":6843,"children":6845},{"className":6844},[],[6846],{"type":44,"value":6847},"ChannelType",{"type":44,"value":6849}," identifiers appear anywhere in the codebase. If absent, skip DATA rules.",{"type":39,"tag":76,"props":6851,"children":6852},{},[6853,6858,6863],{"type":39,"tag":80,"props":6854,"children":6855},{},[6856],{"type":44,"value":6857},"Search for analytics, reporting, dashboard, aggregate query code — check if it reads from multiple channels without access gating",{"type":39,"tag":80,"props":6859,"children":6860},{},[6861],{"type":44,"value":6862},"Search for broadcast or cross-post patterns",{"type":39,"tag":80,"props":6864,"children":6865},{},[6866,6871,6872,6877,6878,6883,6885,6891,6892,6898,6899,6905,6907,6912,6914,6919,6921],{"type":39,"tag":47,"props":6867,"children":6868},{},[6869],{"type":44,"value":6870},"Flag as Bucket 1 violation (DATA-3 CRITICAL):",{"type":44,"value":2786},{"type":39,"tag":67,"props":6873,"children":6875},{"className":6874},[],[6876],{"type":44,"value":4480},{"type":44,"value":1952},{"type":39,"tag":67,"props":6879,"children":6881},{"className":6880},[],[6882],{"type":44,"value":4473},{"type":44,"value":6884}," used in ",{"type":39,"tag":67,"props":6886,"children":6888},{"className":6887},[],[6889],{"type":44,"value":6890},"if",{"type":44,"value":3660},{"type":39,"tag":67,"props":6893,"children":6895},{"className":6894},[],[6896],{"type":44,"value":6897},"switch",{"type":44,"value":3660},{"type":39,"tag":67,"props":6900,"children":6902},{"className":6901},[],[6903],{"type":44,"value":6904},"case",{"type":44,"value":6906}," branching to select different code paths, different API calls, or different access decisions. Quote the switch\u002Fcase block. The fix is to replace all branches with a single ",{"type":39,"tag":67,"props":6908,"children":6910},{"className":6909},[],[6911],{"type":44,"value":660},{"type":44,"value":6913}," (or ",{"type":39,"tag":67,"props":6915,"children":6917},{"className":6916},[],[6918],{"type":44,"value":689},{"type":44,"value":6920},") call that works for every channel type uniformly.\n",{"type":39,"tag":76,"props":6922,"children":6923},{},[6924],{"type":39,"tag":80,"props":6925,"children":6926},{},[6927],{"type":44,"value":6928},"Exception: reading the value for display-only purposes (e.g., showing a label \"Private channel\") is acceptable and should NOT be flagged.",{"type":39,"tag":100,"props":6930,"children":6931},{},[],{"type":39,"tag":424,"props":6933,"children":6935},{"id":6934},"graph-subscriptions",[6936],{"type":44,"value":386},{"type":39,"tag":40,"props":6938,"children":6939},{},[6940,6944,6946,6952,6953,6958,6959,6964,6965,6971],{"type":39,"tag":47,"props":6941,"children":6942},{},[6943],{"type":44,"value":5958},{"type":44,"value":6945}," No Graph change notification or subscription code exists (search for ",{"type":39,"tag":67,"props":6947,"children":6949},{"className":6948},[],[6950],{"type":44,"value":6951},"subscriptionUrl",{"type":44,"value":662},{"type":39,"tag":67,"props":6954,"children":6956},{"className":6955},[],[6957],{"type":44,"value":4703},{"type":44,"value":662},{"type":39,"tag":67,"props":6960,"children":6962},{"className":6961},[],[6963],{"type":44,"value":4666},{"type":44,"value":662},{"type":39,"tag":67,"props":6966,"children":6968},{"className":6967},[],[6969],{"type":44,"value":6970},"\u002Fsubscriptions",{"type":44,"value":6972},"). If absent, skip all SUB rules.",{"type":39,"tag":40,"props":6974,"children":6975},{},[6976],{"type":39,"tag":47,"props":6977,"children":6978},{},[6979],{"type":44,"value":5996},{"type":39,"tag":76,"props":6981,"children":6982},{},[6983,6995,7007,7025],{"type":39,"tag":80,"props":6984,"children":6985},{},[6986,6988,6993],{"type":44,"value":6987},"Subscription resource pointing to ",{"type":39,"tag":67,"props":6989,"children":6991},{"className":6990},[],[6992],{"type":44,"value":1159},{"type":44,"value":6994}," — wrong endpoint",{"type":39,"tag":80,"props":6996,"children":6997},{},[6998,7000,7005],{"type":44,"value":6999},"Missing ",{"type":39,"tag":67,"props":7001,"children":7003},{"className":7002},[],[7004],{"type":44,"value":4583},{"type":44,"value":7006}," in membership subscription resource",{"type":39,"tag":80,"props":7008,"children":7009},{},[7010,7011,7016,7018,7023],{"type":44,"value":6999},{"type":39,"tag":67,"props":7012,"children":7014},{"className":7013},[],[7015],{"type":44,"value":5163},{"type":44,"value":7017}," when ",{"type":39,"tag":67,"props":7019,"children":7021},{"className":7020},[],[7022],{"type":44,"value":4802},{"type":44,"value":7024}," is set to more than 1 hour out",{"type":39,"tag":80,"props":7026,"children":7027},{},[7028,7030,7035],{"type":44,"value":7029},"No ",{"type":39,"tag":67,"props":7031,"children":7033},{"className":7032},[],[7034],{"type":44,"value":4884},{"type":44,"value":7036}," subscription when the app handles bulk membership events",{"type":39,"tag":100,"props":7038,"children":7039},{},[],{"type":39,"tag":424,"props":7041,"children":7043},{"id":7042},"context-api",[7044],{"type":44,"value":408},{"type":39,"tag":40,"props":7046,"children":7047},{},[7048,7052,7054,7059,7060,7066],{"type":39,"tag":47,"props":7049,"children":7050},{},[7051],{"type":44,"value":5958},{"type":44,"value":7053}," No ",{"type":39,"tag":67,"props":7055,"children":7057},{"className":7056},[],[7058],{"type":44,"value":1727},{"type":44,"value":662},{"type":39,"tag":67,"props":7061,"children":7063},{"className":7062},[],[7064],{"type":44,"value":7065},"microsoftTeams.app.getContext",{"type":44,"value":7067},", or Teams JS SDK context calls exist in the frontend code. If absent, skip all CTX rules.",{"type":39,"tag":76,"props":7069,"children":7070},{},[7071,7080,7090],{"type":39,"tag":80,"props":7072,"children":7073},{},[7074,7079],{"type":39,"tag":67,"props":7075,"children":7077},{"className":7076},[],[7078],{"type":44,"value":1551},{"type":44,"value":6201},{"type":39,"tag":80,"props":7081,"children":7082},{},[7083,7088],{"type":39,"tag":67,"props":7084,"children":7086},{"className":7085},[],[7087],{"type":44,"value":1618},{"type":44,"value":7089}," — confirm it's read for tenant comparisons",{"type":39,"tag":80,"props":7091,"children":7092},{},[7093,7098],{"type":39,"tag":67,"props":7094,"children":7096},{"className":7095},[],[7097],{"type":44,"value":3651},{"type":44,"value":7099}," — confirm it's used for SPO token scoping",{"type":39,"tag":100,"props":7101,"children":7102},{},[],{"type":39,"tag":104,"props":7104,"children":7106},{"id":7105},"report-format",[7107],{"type":44,"value":7108},"Report Format",{"type":39,"tag":40,"props":7110,"children":7111},{},[7112],{"type":44,"value":7113},"Produce the following structured report as an MD file in the root of the repo.",{"type":39,"tag":100,"props":7115,"children":7116},{},[],{"type":39,"tag":104,"props":7118,"children":7120},{"id":7119},"ngc-channel-readiness-report",[7121],{"type":44,"value":51},{"type":39,"tag":40,"props":7123,"children":7124},{},[7125,7130,7131,7136,7141,7142],{"type":39,"tag":47,"props":7126,"children":7127},{},[7128],{"type":44,"value":7129},"App:",{"type":44,"value":2786},{"type":39,"tag":476,"props":7132,"children":7133},{},[7134],{"type":44,"value":7135},"name or root directory of the scanned codebase",{"type":39,"tag":47,"props":7137,"children":7138},{},[7139],{"type":44,"value":7140},"Scanned:",{"type":44,"value":2786},{"type":39,"tag":476,"props":7143,"children":7144},{},[7145],{"type":44,"value":7146},"today's date",{"type":39,"tag":100,"props":7148,"children":7149},{},[],{"type":39,"tag":55,"props":7151,"children":7153},{"id":7152},"overall-readiness",[7154],{"type":44,"value":7155},"Overall Readiness",{"type":39,"tag":40,"props":7157,"children":7158},{},[7159],{"type":44,"value":7160},"State one of the following verdicts, bold and prominent:",{"type":39,"tag":206,"props":7162,"children":7163},{},[7164,7180],{"type":39,"tag":210,"props":7165,"children":7166},{},[7167],{"type":39,"tag":214,"props":7168,"children":7169},{},[7170,7175],{"type":39,"tag":218,"props":7171,"children":7172},{},[7173],{"type":44,"value":7174},"Verdict",{"type":39,"tag":218,"props":7176,"children":7177},{},[7178],{"type":44,"value":7179},"Criteria",{"type":39,"tag":234,"props":7181,"children":7182},{},[7183,7209,7232,7250],{"type":39,"tag":214,"props":7184,"children":7185},{},[7186,7196],{"type":39,"tag":241,"props":7187,"children":7188},{},[7189,7191],{"type":44,"value":7190},"✅ ",{"type":39,"tag":47,"props":7192,"children":7193},{},[7194],{"type":44,"value":7195},"Channel Ready",{"type":39,"tag":241,"props":7197,"children":7198},{},[7199,7201,7207],{"type":44,"value":7200},"Zero confirmed violations. Manifest has ",{"type":39,"tag":67,"props":7202,"children":7204},{"className":7203},[],[7205],{"type":44,"value":7206},"supportsChannelFeatures: tier1",{"type":44,"value":7208}," and correct manifest version.",{"type":39,"tag":214,"props":7210,"children":7211},{},[7212,7220],{"type":39,"tag":241,"props":7213,"children":7214},{},[7215,7216],{"type":44,"value":7190},{"type":39,"tag":47,"props":7217,"children":7218},{},[7219],{"type":44,"value":5713},{"type":39,"tag":241,"props":7221,"children":7222},{},[7223,7225,7230],{"type":44,"value":7224},"App has no ",{"type":39,"tag":67,"props":7226,"children":7228},{"className":7227},[],[7229],{"type":44,"value":5643},{"type":44,"value":7231}," scope in any manifest element. NGC rules do not apply.",{"type":39,"tag":214,"props":7233,"children":7234},{},[7235,7245],{"type":39,"tag":241,"props":7236,"children":7237},{},[7238,7240],{"type":44,"value":7239},"⚠️ ",{"type":39,"tag":47,"props":7241,"children":7242},{},[7243],{"type":44,"value":7244},"Needs Minor Changes",{"type":39,"tag":241,"props":7246,"children":7247},{},[7248],{"type":44,"value":7249},"No CRITICAL violations. A few HIGH rules need attention. Estimated effort of 2 to 3 days.",{"type":39,"tag":214,"props":7251,"children":7252},{},[7253,7263],{"type":39,"tag":241,"props":7254,"children":7255},{},[7256,7258],{"type":44,"value":7257},"🔴 ",{"type":39,"tag":47,"props":7259,"children":7260},{},[7261],{"type":44,"value":7262},"Needs Major Changes",{"type":39,"tag":241,"props":7264,"children":7265},{},[7266],{"type":44,"value":7267},"One or more CRITICAL violations. Core APIs or manifest need rework. Estimated effort 1+ weeks.",{"type":39,"tag":40,"props":7269,"children":7270},{},[7271,7273,7278],{"type":44,"value":7272},"Follow the verdict with 2–4 sentences explaining ",{"type":39,"tag":2788,"props":7274,"children":7275},{},[7276],{"type":44,"value":7277},"why",{"type":44,"value":7279}," the app is in this state, naming the dominant failure categories.",{"type":39,"tag":100,"props":7281,"children":7282},{},[],{"type":39,"tag":55,"props":7284,"children":7286},{"id":7285},"bucket-1-confirmed-violations",[7287],{"type":44,"value":7288},"Bucket 1 — Confirmed Violations",{"type":39,"tag":111,"props":7290,"children":7291},{},[7292],{"type":39,"tag":40,"props":7293,"children":7294},{},[7295,7297,7302],{"type":44,"value":7296},"These are blatant API misuse or missing declarations that will ",{"type":39,"tag":47,"props":7298,"children":7299},{},[7300],{"type":44,"value":7301},"certainly",{"type":44,"value":7303}," break the app in shared or private channels. Each item must be fixed before the app is NGC-ready.",{"type":39,"tag":40,"props":7305,"children":7306},{},[7307,7312],{"type":39,"tag":47,"props":7308,"children":7309},{},[7310],{"type":44,"value":7311},"Grouping rule — organize by code path, not by rule ID.",{"type":44,"value":7313},"\nGroup all violations that touch the same file or the same logical code area into a single entry. List every applicable rule ID in the header. This makes it easy for a developer to open one file and see all the issues they need to fix in one place. Ordering: CRITICAL entries first, then HIGH.",{"type":39,"tag":40,"props":7315,"children":7316},{},[7317,7322,7323,7328,7329,7335,7336,7342,7344,7349],{"type":39,"tag":47,"props":7318,"children":7319},{},[7320],{"type":44,"value":7321},"When multiple membership API calls are wrong in the same file",{"type":44,"value":6565},{"type":39,"tag":67,"props":7324,"children":7326},{"className":7325},[],[7327],{"type":44,"value":6008},{"type":44,"value":662},{"type":39,"tag":67,"props":7330,"children":7332},{"className":7331},[],[7333],{"type":44,"value":7334},"GetChannelMembersAsync",{"type":44,"value":3564},{"type":39,"tag":67,"props":7337,"children":7339},{"className":7338},[],[7340],{"type":44,"value":7341},"GetChannelMemberAsync",{"type":44,"value":7343}," all need to become ",{"type":39,"tag":67,"props":7345,"children":7347},{"className":7346},[],[7348],{"type":44,"value":660},{"type":44,"value":7350},"), describe them all together in one entry rather than creating a separate entry for each call site.",{"type":39,"tag":40,"props":7352,"children":7353},{},[7354],{"type":44,"value":7355},"For each entry:",{"type":39,"tag":465,"props":7357,"children":7361},{"className":7358,"code":7360,"language":44},[7359],"language-text","### [N]. [File or Logical Code Area] — [Short Violation Title]\n**Severity:** CRITICAL | HIGH  (use the highest severity among all violations in this group)\n**Rules violated:** [e.g., MEM-1, MEM-2, DATA-3]\n**Files:** `path\u002Fto\u002Ffile:line`  (list multiple lines if violations span several locations in the same file)\n\n**What's wrong:**\n[Describe all violations in this code area together. Quote each specific call or pattern. Explain why each fails in shared\u002Fprivate channels. If multiple similar calls have the same root cause (e.g., all need allMembers), state the root cause once and enumerate the affected call sites.]\n\n**Fix:**\n[Consolidated fix addressing all violations in this entry. Correct API, code pattern, manifest field, or SDK method. Include a short snippet if it clarifies the fix.]\n\n**Impact if unresolved:**\n[One sentence on the concrete user-facing or security consequence in shared\u002Fprivate channels.]\n",[7362],{"type":39,"tag":67,"props":7363,"children":7364},{"__ignoreMap":470},[7365],{"type":44,"value":7360},{"type":39,"tag":40,"props":7367,"children":7368},{},[7369,7371],{"type":44,"value":7370},"If there are zero violations total, state: ",{"type":39,"tag":2788,"props":7372,"children":7373},{},[7374],{"type":44,"value":7375},"No confirmed violations found.",{"type":39,"tag":100,"props":7377,"children":7378},{},[],{"type":39,"tag":55,"props":7380,"children":7382},{"id":7381},"bucket-2-suggestions-observations",[7383],{"type":44,"value":7384},"Bucket 2 — Suggestions & Observations",{"type":39,"tag":111,"props":7386,"children":7387},{},[7388],{"type":39,"tag":40,"props":7389,"children":7390},{},[7391,7393,7398],{"type":44,"value":7392},"These are patterns that ",{"type":39,"tag":47,"props":7394,"children":7395},{},[7396],{"type":44,"value":7397},"may or may not",{"type":44,"value":7399}," be issues depending on the app's intended behavior. The app could have valid reasons for these patterns — they are flagged here for review, not as required fixes.",{"type":39,"tag":40,"props":7401,"children":7402},{},[7403],{"type":44,"value":7404},"Use a concise list format. For each item:",{"type":39,"tag":465,"props":7406,"children":7409},{"className":7407,"code":7408,"language":44},[7359],"**[Rule ID] — [Short title]**\n`path\u002Fto\u002Ffile:line`\n[One sentence describing the pattern and why it's worth reviewing. End with a specific suggestion for what to check or consider.]\n",[7410],{"type":39,"tag":67,"props":7411,"children":7412},{"__ignoreMap":470},[7413],{"type":44,"value":7408},{"type":39,"tag":40,"props":7415,"children":7416},{},[7417],{"type":44,"value":7418},"Examples of what belongs here:",{"type":39,"tag":76,"props":7420,"children":7421},{},[7422,7427],{"type":39,"tag":80,"props":7423,"children":7424},{},[7425],{"type":44,"value":7426},"App checks for guest or enterprise guest users and restricts functionality — valid if intentional, but worth reviewing per channel type (shared channels are designed for external collaboration)",{"type":39,"tag":80,"props":7428,"children":7429},{},[7430,7432,7437],{"type":44,"value":7431},"App reads ",{"type":39,"tag":67,"props":7433,"children":7435},{"className":7434},[],[7436],{"type":44,"value":1559},{"type":44,"value":7438}," from context and passes to a backend — may be correct if the backend remaps it; verify the backend uses host team ID for channel-scoped Graph calls",{"type":39,"tag":100,"props":7440,"children":7441},{},[],{"type":39,"tag":55,"props":7443,"children":7445},{"id":7444},"bucket-3-futuristic-migration-prep",[7446],{"type":44,"value":7447},"Bucket 3 — Futuristic \u002F Migration Prep",{"type":39,"tag":111,"props":7449,"children":7450},{},[7451],{"type":39,"tag":40,"props":7452,"children":7453},{},[7454,7456,7461],{"type":44,"value":7455},"These are ",{"type":39,"tag":47,"props":7457,"children":7458},{},[7459],{"type":44,"value":7460},"not current violations",{"type":44,"value":7462}," — the code does not use these APIs yet. They are changes that will be required when migrating to NGC-compliant patterns, or that would improve correctness and resilience once the Bucket 1 fixes are in place.",{"type":39,"tag":40,"props":7464,"children":7465},{},[7466],{"type":44,"value":7404},{"type":39,"tag":465,"props":7468,"children":7471},{"className":7469,"code":7470,"language":44},[7359],"**[Rule ID] — [Short title]**\n[One sentence describing what needs to be added or changed, and when it becomes relevant (e.g., \"Required when adopting allMembers API\").]\n",[7472],{"type":39,"tag":67,"props":7473,"children":7474},{"__ignoreMap":470},[7475],{"type":44,"value":7470},{"type":39,"tag":40,"props":7477,"children":7478},{},[7479],{"type":44,"value":7418},{"type":39,"tag":76,"props":7481,"children":7482},{},[7483,7494,7512],{"type":39,"tag":80,"props":7484,"children":7485},{},[7486,7487,7492],{"type":44,"value":448},{"type":39,"tag":67,"props":7488,"children":7490},{"className":7489},[],[7491],{"type":44,"value":652},{"type":44,"value":7493}," RSC permission to manifest — required when moving from service-auth Graph calls to RSC-based channel membership APIs",{"type":39,"tag":80,"props":7495,"children":7496},{},[7497,7499,7504,7505,7510],{"type":44,"value":7498},"Extract ",{"type":39,"tag":67,"props":7500,"children":7502},{"className":7501},[],[7503],{"type":44,"value":1551},{"type":44,"value":1632},{"type":39,"tag":67,"props":7506,"children":7508},{"className":7507},[],[7509],{"type":44,"value":1587},{"type":44,"value":7511}," and pipe to backend — required alongside any allMembers migration",{"type":39,"tag":80,"props":7513,"children":7514},{},[7515,7517,7522],{"type":44,"value":7516},"Inspect ",{"type":39,"tag":67,"props":7518,"children":7520},{"className":7519},[],[7521],{"type":44,"value":1225},{"type":44,"value":7523}," on allMembers responses — needed after allMembers adoption to correctly classify direct vs indirect members and avoid double-counting",{"type":39,"tag":100,"props":7525,"children":7526},{},[],{"type":39,"tag":55,"props":7528,"children":7530},{"id":7529},"effort-estimate-bucket-1-only",[7531],{"type":44,"value":7532},"Effort Estimate (Bucket 1 only)",{"type":39,"tag":206,"props":7534,"children":7535},{},[7536,7557],{"type":39,"tag":210,"props":7537,"children":7538},{},[7539],{"type":39,"tag":214,"props":7540,"children":7541},{},[7542,7547,7552],{"type":39,"tag":218,"props":7543,"children":7544},{},[7545],{"type":44,"value":7546},"Area",{"type":39,"tag":218,"props":7548,"children":7549},{},[7550],{"type":44,"value":7551},"Violations",{"type":39,"tag":218,"props":7553,"children":7554},{},[7555],{"type":44,"value":7556},"Effort",{"type":39,"tag":234,"props":7558,"children":7559},{},[7560,7573,7586,7600,7614,7628,7641,7654,7667],{"type":39,"tag":214,"props":7561,"children":7562},{},[7563,7567,7570],{"type":39,"tag":241,"props":7564,"children":7565},{},[7566],{"type":44,"value":254},{"type":39,"tag":241,"props":7568,"children":7569},{},[],{"type":39,"tag":241,"props":7571,"children":7572},{},[],{"type":39,"tag":214,"props":7574,"children":7575},{},[7576,7580,7583],{"type":39,"tag":241,"props":7577,"children":7578},{},[7579],{"type":44,"value":5878},{"type":39,"tag":241,"props":7581,"children":7582},{},[],{"type":39,"tag":241,"props":7584,"children":7585},{},[],{"type":39,"tag":214,"props":7587,"children":7588},{},[7589,7594,7597],{"type":39,"tag":241,"props":7590,"children":7591},{},[7592],{"type":44,"value":7593},"File \u002F SharePoint",{"type":39,"tag":241,"props":7595,"children":7596},{},[],{"type":39,"tag":241,"props":7598,"children":7599},{},[],{"type":39,"tag":214,"props":7601,"children":7602},{},[7603,7608,7611],{"type":39,"tag":241,"props":7604,"children":7605},{},[7606],{"type":44,"value":7607},"Bot Events",{"type":39,"tag":241,"props":7609,"children":7610},{},[],{"type":39,"tag":241,"props":7612,"children":7613},{},[],{"type":39,"tag":214,"props":7615,"children":7616},{},[7617,7622,7625],{"type":39,"tag":241,"props":7618,"children":7619},{},[7620],{"type":44,"value":7621},"Auth",{"type":39,"tag":241,"props":7623,"children":7624},{},[],{"type":39,"tag":241,"props":7626,"children":7627},{},[],{"type":39,"tag":214,"props":7629,"children":7630},{},[7631,7635,7638],{"type":39,"tag":241,"props":7632,"children":7633},{},[7634],{"type":44,"value":364},{"type":39,"tag":241,"props":7636,"children":7637},{},[],{"type":39,"tag":241,"props":7639,"children":7640},{},[],{"type":39,"tag":214,"props":7642,"children":7643},{},[7644,7648,7651],{"type":39,"tag":241,"props":7645,"children":7646},{},[7647],{"type":44,"value":386},{"type":39,"tag":241,"props":7649,"children":7650},{},[],{"type":39,"tag":241,"props":7652,"children":7653},{},[],{"type":39,"tag":214,"props":7655,"children":7656},{},[7657,7661,7664],{"type":39,"tag":241,"props":7658,"children":7659},{},[7660],{"type":44,"value":408},{"type":39,"tag":241,"props":7662,"children":7663},{},[],{"type":39,"tag":241,"props":7665,"children":7666},{},[],{"type":39,"tag":214,"props":7668,"children":7669},{},[7670,7678,7681],{"type":39,"tag":241,"props":7671,"children":7672},{},[7673],{"type":39,"tag":47,"props":7674,"children":7675},{},[7676],{"type":44,"value":7677},"Total",{"type":39,"tag":241,"props":7679,"children":7680},{},[],{"type":39,"tag":241,"props":7682,"children":7683},{},[7684],{"type":39,"tag":47,"props":7685,"children":7686},{},[7687],{"type":39,"tag":476,"props":7688,"children":7689},{},[7690],{"type":44,"value":7691},"range",{"type":39,"tag":40,"props":7693,"children":7694},{},[7695,7700,7701],{"type":39,"tag":47,"props":7696,"children":7697},{},[7698],{"type":44,"value":7699},"Complexity driver:",{"type":44,"value":2786},{"type":39,"tag":476,"props":7702,"children":7703},{},[7704],{"type":44,"value":7705},"The single biggest source of effort in one sentence",{"type":39,"tag":40,"props":7707,"children":7708},{},[7709],{"type":44,"value":7710},"Generate the report as an MD file in the root folder.",{"type":39,"tag":7712,"props":7713,"children":7714},"style",{},[7715],{"type":44,"value":7716},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":7718,"total":479},[7719],{"slug":4,"name":4,"fn":5,"description":6,"org":7720,"tags":7721,"stars":22,"repoUrl":23,"updatedAt":24},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7722,7723,7724],{"name":20,"slug":21,"type":15},{"name":17,"slug":18,"type":15},{"name":13,"slug":14,"type":15},{"items":7726,"total":7919},[7727,7749,7770,7790,7805,7821,7832,7845,7860,7875,7894,7907],{"slug":7728,"name":7728,"fn":7729,"description":7730,"org":7731,"tags":7732,"stars":7746,"repoUrl":7747,"updatedAt":7748},"rushstack-best-practices","manage Rush monorepos with best practices","Provides best practices and guidance for working with Rush monorepos. Use when the user is working in a Rush-based repository, asks about Rush commands (install, update, build, rebuild), needs help with project selection, dependency management, build caching, subspace configuration, or troubleshooting Rush-specific issues.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7733,7736,7739,7740,7743],{"name":7734,"slug":7735,"type":15},"Engineering","engineering",{"name":7737,"slug":7738,"type":15},"Local Development","local-development",{"name":9,"slug":8,"type":15},{"name":7741,"slug":7742,"type":15},"Project Management","project-management",{"name":7744,"slug":7745,"type":15},"Rush","rush",6484,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Frushstack","2026-04-06T18:34:44.965032",{"slug":7750,"name":7750,"fn":7751,"description":7752,"org":7753,"tags":7754,"stars":7767,"repoUrl":7768,"updatedAt":7769},"azure-ai-agents-persistent-dotnet","build AI agents with Azure .NET SDK","Azure AI Agents Persistent SDK for .NET. Low-level SDK for creating and managing AI agents with threads, messages, runs, and tools. Use for agent CRUD, conversation threads, streaming responses, function calling, file search, and code interpreter. Triggers: \"PersistentAgentsClient\", \"persistent agents\", \"agent threads\", \"agent runs\", \"streaming agents\", \"function calling agents .NET\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7755,7758,7761,7764],{"name":7756,"slug":7757,"type":15},".NET","net",{"name":7759,"slug":7760,"type":15},"Agents","agents",{"name":7762,"slug":7763,"type":15},"Azure","azure",{"name":7765,"slug":7766,"type":15},"LLM","llm",2804,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fskills","2026-07-03T16:32:10.297433",{"slug":7771,"name":7771,"fn":7772,"description":7773,"org":7774,"tags":7775,"stars":7767,"repoUrl":7768,"updatedAt":7789},"azure-ai-anomalydetector-java","build anomaly detection applications with Java","Build anomaly detection applications with Azure AI Anomaly Detector SDK for Java. Use when implementing univariate\u002Fmultivariate anomaly detection, time-series analysis, or AI-powered monitoring.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7776,7779,7780,7783,7785,7786],{"name":7777,"slug":7778,"type":15},"Analytics","analytics",{"name":7762,"slug":7763,"type":15},{"name":7781,"slug":7782,"type":15},"Data Analysis","data-analysis",{"name":1338,"slug":7784,"type":15},"java",{"name":9,"slug":8,"type":15},{"name":7787,"slug":7788,"type":15},"Monitoring","monitoring","2026-05-13T06:14:16.261754",{"slug":7791,"name":7791,"fn":7792,"description":7793,"org":7794,"tags":7795,"stars":7767,"repoUrl":7768,"updatedAt":7804},"azure-ai-contentsafety-java","build content moderation applications with Azure AI","Build content moderation applications with Azure AI Content Safety SDK for Java. Use when implementing text\u002Fimage analysis, blocklist management, or harm detection for hate, violence, sexual content, and self-harm.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7796,7799,7800,7801],{"name":7797,"slug":7798,"type":15},"AI Infrastructure","ai-infrastructure",{"name":7762,"slug":7763,"type":15},{"name":1338,"slug":7784,"type":15},{"name":7802,"slug":7803,"type":15},"Security","security","2026-07-07T06:53:31.293235",{"slug":7806,"name":7806,"fn":7807,"description":7808,"org":7809,"tags":7810,"stars":7767,"repoUrl":7768,"updatedAt":7820},"azure-ai-contentsafety-py","detect harmful content with Azure AI Content Safety","Azure AI Content Safety SDK for Python. Use for detecting harmful content in text and images with multi-severity classification.\nTriggers: \"azure-ai-contentsafety\", \"ContentSafetyClient\", \"content moderation\", \"harmful content\", \"text analysis\", \"image analysis\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7811,7812,7815,7816,7817,7819],{"name":7762,"slug":7763,"type":15},{"name":7813,"slug":7814,"type":15},"Compliance","compliance",{"name":7765,"slug":7766,"type":15},{"name":9,"slug":8,"type":15},{"name":1364,"slug":7818,"type":15},"python",{"name":7802,"slug":7803,"type":15},"2026-07-18T05:14:23.017504",{"slug":7822,"name":7822,"fn":7823,"description":7824,"org":7825,"tags":7826,"stars":7767,"repoUrl":7768,"updatedAt":7831},"azure-ai-language-conversations-py","implement conversational language understanding with Python","Implement Conversational Language Understanding (CLU) using the azure-ai-language-conversations Python SDK. Use when working with ConversationAnalysisClient to analyze conversation intent and entities, building NLP features, or integrating language understanding into applications.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7827,7828,7829,7830],{"name":7777,"slug":7778,"type":15},{"name":7762,"slug":7763,"type":15},{"name":7765,"slug":7766,"type":15},{"name":1364,"slug":7818,"type":15},"2026-07-31T05:54:29.068751",{"slug":7833,"name":7833,"fn":7834,"description":7835,"org":7836,"tags":7837,"stars":7767,"repoUrl":7768,"updatedAt":7844},"azure-ai-translation-text-py","translate text using Azure AI services","Azure AI Text Translation SDK for real-time text translation, transliteration, language detection, and dictionary lookup. Use for translating text content in applications.\nTriggers: \"text translation\", \"translator\", \"translate text\", \"transliterate\", \"TextTranslationClient\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7838,7841,7842,7843],{"name":7839,"slug":7840,"type":15},"API Development","api-development",{"name":7762,"slug":7763,"type":15},{"name":9,"slug":8,"type":15},{"name":1364,"slug":7818,"type":15},"2026-07-18T05:14:16.988376",{"slug":7846,"name":7846,"fn":7847,"description":7848,"org":7849,"tags":7850,"stars":7767,"repoUrl":7768,"updatedAt":7859},"azure-ai-vision-imageanalysis-py","analyze images with Azure AI Vision","Azure AI Vision Image Analysis SDK for captions, tags, objects, OCR, people detection, and smart cropping. Use for computer vision and image understanding tasks.\nTriggers: \"image analysis\", \"computer vision\", \"OCR\", \"object detection\", \"ImageAnalysisClient\", \"image caption\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7851,7852,7855,7858],{"name":7762,"slug":7763,"type":15},{"name":7853,"slug":7854,"type":15},"Computer Vision","computer-vision",{"name":7856,"slug":7857,"type":15},"Images","images",{"name":1364,"slug":7818,"type":15},"2026-07-18T05:14:18.007737",{"slug":7861,"name":7861,"fn":7862,"description":7863,"org":7864,"tags":7865,"stars":7767,"repoUrl":7768,"updatedAt":7874},"azure-appconfiguration-java","manage configuration with Azure App Configuration","Azure App Configuration SDK for Java. Centralized application configuration management with key-value settings, feature flags, and snapshots.\nTriggers: \"ConfigurationClient java\", \"app configuration java\", \"feature flag java\", \"configuration setting java\", \"azure config java\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7866,7867,7870,7873],{"name":7762,"slug":7763,"type":15},{"name":7868,"slug":7869,"type":15},"Configuration","configuration",{"name":7871,"slug":7872,"type":15},"Feature Flags","feature-flags",{"name":1338,"slug":7784,"type":15},"2026-07-03T16:32:01.278468",{"slug":7876,"name":7876,"fn":7877,"description":7878,"org":7879,"tags":7880,"stars":7767,"repoUrl":7768,"updatedAt":7893},"azure-cosmos-rust","build applications with Azure Cosmos DB","Azure Cosmos DB library for Rust (NoSQL API). Document CRUD, containers, and globally distributed data.\nTriggers: \"cosmos db rust\", \"CosmosClient rust\", \"document crud rust\", \"NoSQL rust\", \"partition key rust\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7881,7884,7887,7890],{"name":7882,"slug":7883,"type":15},"Cosmos DB","cosmos-db",{"name":7885,"slug":7886,"type":15},"Database","database",{"name":7888,"slug":7889,"type":15},"NoSQL","nosql",{"name":7891,"slug":7892,"type":15},"Rust","rust","2026-07-31T05:54:27.021432",{"slug":7895,"name":7895,"fn":7877,"description":7896,"org":7897,"tags":7898,"stars":7767,"repoUrl":7768,"updatedAt":7906},"azure-cosmos-ts","Azure Cosmos DB JavaScript\u002FTypeScript SDK (@azure\u002Fcosmos) for data plane operations. Use for CRUD operations on documents, queries, bulk operations, and container management. Triggers: \"Cosmos DB\", \"@azure\u002Fcosmos\", \"CosmosClient\", \"document CRUD\", \"NoSQL queries\", \"bulk operations\", \"partition key\", \"container.items\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7899,7900,7901,7902,7903],{"name":7882,"slug":7883,"type":15},{"name":7885,"slug":7886,"type":15},{"name":9,"slug":8,"type":15},{"name":7888,"slug":7889,"type":15},{"name":7904,"slug":7905,"type":15},"TypeScript","typescript","2026-07-03T16:31:19.368382",{"slug":7908,"name":7908,"fn":7909,"description":7910,"org":7911,"tags":7912,"stars":7767,"repoUrl":7768,"updatedAt":7918},"azure-data-tables-java","build table storage applications with Java","Build table storage applications with Azure Tables SDK for Java. Use when working with Azure Table Storage or Cosmos DB Table API for NoSQL key-value data, schemaless storage, or structured data at scale.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[7913,7914,7915,7916,7917],{"name":7762,"slug":7763,"type":15},{"name":7882,"slug":7883,"type":15},{"name":7885,"slug":7886,"type":15},{"name":1338,"slug":7784,"type":15},{"name":7888,"slug":7889,"type":15},"2026-05-13T06:14:17.582229",267]