[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69BSPMFSH3FAR4R5TGDB468":3,"skill-same:01M69BSPMFSH3FAR4R5TGDB468":67},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":39,"linkOnly":40,"markdownContent":41,"markdownBody":42,"frontMatter":43,"refName":44,"commitSha":45,"skillDirectory":46,"forksCount":47,"repositoryHtmlUrl":48,"repositoryHomepageUrl":6,"ownerHtmlUrl":49,"ownerBlogUrl":50,"ownerDescription":51,"updatedAt":52,"install":53,"resources":66},"01M69BSPMFSH3FAR4R5TGDB468","telemetry-instrumentation",null,"Instrumentation patterns for telemetry in the vscode-documentdb extension. Use when adding telemetry to a feature, enriching existing telemetry with properties or measurements, linking related events with correlation IDs, instrumenting commands or tree views, suppressing noisy events, or deciding what data points to capture. Also use when asked to \"add telemetry\" or \"improve telemetry\" for any feature.","microsoft\u002Fvscode-documentdb","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M69BRX0BQJ0QGRNCNZAAAXBV",33,"MIT","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftelemetry-instrumentation\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38],"telemetry","telemetry instrumentation","instrumentation","when","vscode","Existing","linking","for","tree","with","add","events","feature","commands","patterns","properties","use","improve","ids","data","2026-11-10 10:12:30",false,"---\nname: telemetry-instrumentation\ndescription: Instrumentation patterns for telemetry in the vscode-documentdb extension. Use when adding telemetry to a feature, enriching existing telemetry with properties or measurements, linking related events with correlation IDs, instrumenting commands or tree views, suppressing noisy events, or deciding what data points to capture. Also use when asked to \"add telemetry\" or \"improve telemetry\" for any feature.\n---\n\n# Telemetry Instrumentation\n\nHow to instrument code in vscode-documentdb to produce actionable telemetry.\n\n## What the Framework Gives You for Free\n\nThe `@microsoft\u002Fvscode-azext-utils` library **automatically captures** these — never add them manually:\n\n| Auto-captured           | Details                                                            |\n| ----------------------- | ------------------------------------------------------------------ |\n| **Duration**            | `measurements.duration` — wall-clock ms from callback start to end |\n| **Result**              | `properties.result` — `\"Succeeded\"`, `\"Failed\"`, or `\"Canceled\"`   |\n| **Error info**          | `properties.error` (name), `properties.errorMessage` (message)     |\n| **Cancel detection**    | `UserCancelledError` sets result to `\"Canceled\"` automatically     |\n| **Machine\u002Fsession IDs** | `VSCodeMachineId`, `VSCodeSessionId` — always present              |\n| **Extension metadata**  | Extension name, version, VS Code version                           |\n\n**Do not** re-measure duration, set `result`, or catch errors just for telemetry — the wrapper handles it.\n\n## Core API\n\n### `callWithTelemetryAndErrorHandling(eventId, callback)`\n\nThe primary wrapper. Creates a telemetry event, runs the callback, reports result + duration + errors. The callback's return value is passed through, so you can wrap any function call directly:\n\n```typescript\nimport { callWithTelemetryAndErrorHandling } from '@microsoft\u002Fvscode-azext-utils';\n\n\u002F\u002F Simple — wrap logic, telemetry is automatic\nawait callWithTelemetryAndErrorHandling('myFeature.doSomething', async (context) => {\n  context.telemetry.properties.someProperty = 'value';\n  context.telemetry.measurements.itemCount = items.length;\n  \u002F\u002F ... your logic — errors are caught and reported automatically\n});\n\n\u002F\u002F With return value — no need for intermediate variables\nconst result = await callWithTelemetryAndErrorHandling('myFeature.query', async (context) => {\n  context.telemetry.properties.queryType = 'find';\n  return doWork(); \u002F\u002F return value passes through to the caller\n});\n```\n\n**Event name convention**: use dot-separated hierarchy: `connect`, `connect.getmetadata`, `connect.promptForCredentials`.\n\n### Command Registration (auto-telemetry)\n\nEvery registered command gets a telemetry event automatically. Prefer the correlation-wrapped variants:\n\n```typescript\nimport { registerCommandWithTreeNodeUnwrapping } from '@microsoft\u002Fvscode-azext-utils';\nimport { withTreeNodeCommandCorrelation, withCommandCorrelation } from '..\u002F..\u002Futils\u002FcommandTelemetry';\n\n\u002F\u002F Tree node commands — unwraps tree item + tracks journeyCorrelationId\nregisterCommandWithTreeNodeUnwrapping(\n  'vscode-documentdb.command.connectionsView.myCommand',\n  withTreeNodeCommandCorrelation(myCommandHandler),\n);\n\n\u002F\u002F Plain commands — tracks journeyCorrelationId\nregisterCommand('vscode-documentdb.command.myCommand', withCommandCorrelation(myCommandHandler));\n```\n\nFor critical commands where errors need modal dialogs:\n\n```typescript\nimport { registerCommandWithTreeNodeUnwrappingAndModalErrors } from '..\u002F..\u002Futils\u002FcommandErrorHandling';\n```\n\n### tRPC Procedures (auto-telemetry)\n\nWebview tRPC calls get telemetry automatically via `publicProcedureWithTelemetry`. Each call emits `documentDB.rpc.{type}.{path}` with result, error, abort tracking. The DocumentDB `TelemetryRunner` contributes the full `IActionContext` to `ctx.actionContext` (narrow `ctx` to `WithTelemetry\u003CRouterContext>` to read it):\n\n```typescript\nmyProcedure: publicProcedureWithTelemetry.input(schema).query(async ({ ctx, input }) => {\n    const myCtx = ctx as WithTelemetry\u003CRouterContext>;\n    myCtx.actionContext.telemetry.properties.someDetail = 'value';\n    myCtx.actionContext.telemetry.measurements.resultCount = results.length;\n    return results;\n}),\n```\n\n## Naming Conventions\n\n### Property & measurement names\n\n- **Default**: use **camelCase** — `authMethod`, `connectionMode`, `discoveryProviderId`\n- **Namespaced metadata**: when spreading a flat metadata object into telemetry (e.g., cluster metadata collected once and reused), use **prefix_camelCase** — `domainInfo_isAzure`, `serverInfo_version`. The prefix acts as a namespace. This pattern exists in `getClusterMetadata.ts` and is matched by dashboard queries.\n- **Never mix** within one prefix: `task_initializationCompleted` (mixed snake + camelCase) is wrong — use `task_initializationCompleted` → `taskInitializationCompleted` or `task_initialization_completed` consistently.\n- **Correlation IDs**: always use camelCase — `journeyCorrelationId`, `connectionCorrelationId`.\n\n### Boolean properties\n\n**Always use the ternary pattern** — it makes the intent explicit:\n\n```typescript\n\u002F\u002F ✅ Correct — clear intent, consistent\ncontext.telemetry.properties.hasFolders = totalFolders > 0 ? 'true' : 'false';\ncontext.telemetry.properties.isFirstConnection = isFirst ? 'true' : 'false';\n\n\u002F\u002F ❌ Avoid — works but less readable, can accidentally stringify non-booleans\ncontext.telemetry.properties.isWindows = isWindows.toString();\n```\n\n### Numbers: property or measurement?\n\n**Rule: if you'd ever want to compute P50, average, sum, or max over it, use a measurement.** Numbers stored as string properties can't be aggregated in analytics without casting.\n\n```typescript\n\u002F\u002F ✅ Measurement — enables P50(pageNumber), avg(itemCount)\ncontext.telemetry.measurements.pageNumber = input.pageNumber;\ncontext.telemetry.measurements.itemCount = items.length;\n\n\u002F\u002F ❌ Wrong — forces dashboard to todouble(Properties.pageNumber)\ncontext.telemetry.properties.pageNumber = input.pageNumber.toString();\n```\n\n**Exception**: numeric values used purely as categories (e.g., an error code used for grouping, not aggregation) can be properties.\n\n## Properties vs Measurements\n\n| Use                       | Type                                 | Example                                          |\n| ------------------------- | ------------------------------------ | ------------------------------------------------ |\n| **Properties** (string)   | Categorical data, flags, identifiers | `serverType`, `view`, `authMethod`, `hasFolders` |\n| **Measurements** (number) | Counts, sizes, durations, rates      | `itemCount`, `loadTimeMs`, `retryAttempts`       |\n\n### Properties — what to capture\n\n```typescript\n\u002F\u002F Feature context — what variant\u002Fmode is being used\ncontext.telemetry.properties.view = 'connectionsView';\ncontext.telemetry.properties.experience = node.experience.api; \u002F\u002F 'vCore' | 'RU'\ncontext.telemetry.properties.authMethod = 'connectionString';\n\n\u002F\u002F Boolean flags — always use 'true'\u002F'false' strings\ncontext.telemetry.properties.hasFolders = totalFolders > 0 ? 'true' : 'false';\ncontext.telemetry.properties.isFirstConnection = isFirst ? 'true' : 'false';\n\n\u002F\u002F Classification of inputs or results\ncontext.telemetry.properties.connectionMode = 'documentdb'; \u002F\u002F what was selected\ncontext.telemetry.properties.terminalType = 'PowerShell'; \u002F\u002F what environment\n\n\u002F\u002F Error classification beyond the auto-captured error\ncontext.telemetry.properties.connectionErrorType = 'auth'; \u002F\u002F error category\ncontext.telemetry.properties.parseError = 'SyntaxError'; \u002F\u002F specific error type\n\n\u002F\u002F View identification — set for user-initiated or view-specific operations\n\u002F\u002F Skip for background tasks, activation events, or cross-cutting concerns\ncontext.telemetry.properties.view = Views.ConnectionsView;\n```\n\n### Measurements — what to capture\n\n```typescript\n\u002F\u002F Counts — how many items were involved\ncontext.telemetry.measurements.itemCount = results.length;\ncontext.telemetry.measurements.totalConnections = connections.length;\ncontext.telemetry.measurements.savedConnections = rootItems.length;\n\n\u002F\u002F Requested vs returned — reveals pagination, limits, empty results\ncontext.telemetry.measurements.pageSize = input.pageSize;\ncontext.telemetry.measurements.documentCount = docs.length;\n\n\u002F\u002F Sub-durations — where time is spent (the total is auto-captured)\ncontext.telemetry.measurements.loadTimeMs = Date.now() - startTime;\ncontext.telemetry.measurements.mainFileLoad = (loadEnd - loadStart) \u002F 1000;\n\n\u002F\u002F Attempts\u002Fretries\ncontext.telemetry.measurements.connectionViewActivationAttempts = attempt + 1;\ncontext.telemetry.measurements.cleanupIterations = iteration;\n\n\u002F\u002F Breakdown counts — for multi-category results\ncontext.telemetry.measurements.createRecommendationCount = createCount;\ncontext.telemetry.measurements.dropRecommendationCount = dropCount;\n\n\u002F\u002F Structural complexity — depth, nesting\ncontext.telemetry.measurements.maxFolderDepth = maxDepth;\n```\n\n### Dynamic property\u002Fmeasurement names\n\nWhen the set of categories is data-driven, use a prefix:\n\n```typescript\ncontext.telemetry.measurements[`${zonePrefix}_Connections`] = connectionsInZone;\ncontext.telemetry.measurements[`${zonePrefix}_Folders`] = foldersInZone;\n```\n\n## Sub-step Telemetry\n\nUse nested `callWithTelemetryAndErrorHandling` inside a parent event to get fine-grained timing and success\u002Ffailure per step:\n\n```typescript\nawait callWithTelemetryAndErrorHandling('connect', async (context) => {\n  context.telemetry.properties.view = 'discoveryView';\n\n  \u002F\u002F Sub-step: prompting — gets its own duration, result, errors\n  await callWithTelemetryAndErrorHandling('connect.promptForCredentials', async (subCtx) => {\n    subCtx.errorHandling.rethrow = true; \u002F\u002F let parent see failures\n    await wizard.prompt();\n  });\n\n  \u002F\u002F Sub-step: metadata collection (fire-and-forget, non-blocking)\n  void callWithTelemetryAndErrorHandling('connect.getmetadata', async (metaCtx) => {\n    const metadata = await collectMetadata();\n    metaCtx.telemetry.properties = { ...metaCtx.telemetry.properties, ...metadata };\n  });\n});\n```\n\n**When to use sub-steps**:\n\n- Operation has distinct phases (prompt → connect → collect metadata)\n- You need per-phase duration\u002Fsuccess data\n- A sub-step can fail independently without failing the parent\n\n**Naming**: use parent event as prefix: `connect.promptForCredentials`, `connect.getmetadata`, `connect.staticmetadata`.\n\n## Correlation IDs — Linking Related Events\n\n### journeyCorrelationId — user journey across commands\n\nLinks a chain of user actions that form a logical journey (e.g., discover → select → connect → browse). Generated once at the start and propagated through tree items:\n\n```typescript\n\u002F\u002F Generate at the journey start (e.g., when a discovery tree root is created)\nthis.journeyCorrelationId = randomUUID();\n\n\u002F\u002F Tree items carry it to children\nnew MongoRUResourceItem(this, account, this.journeyCorrelationId);\n\n\u002F\u002F Commands pick it up automatically via withTreeNodeCommandCorrelation\u002FwithCommandCorrelation\n\u002F\u002F OR manually in a callWithTelemetryAndErrorHandling:\nif (this.journeyCorrelationId) {\n  context.telemetry.properties.journeyCorrelationId = this.journeyCorrelationId;\n}\n```\n\n**Use `journeyCorrelationId` when**: a user flow spans multiple command invocations that should be analyzed together (discovery → connect → browse, folder operations in sequence).\n\n### connectionCorrelationId — linking connection sub-events\n\nLinks the `connect`, `connect.staticmetadata`, and `connect.getmetadata` events for a single connection attempt:\n\n```typescript\nthis.connectionCorrelationId = randomUUID();\n\nvoid callWithTelemetryAndErrorHandling('connect.staticmetadata', async (context) => {\n  context.telemetry.properties.connectionCorrelationId = this.connectionCorrelationId;\n  \u002F\u002F ...domain metadata\n});\n\nvoid callWithTelemetryAndErrorHandling('connect.getmetadata', async (context) => {\n  context.telemetry.properties.connectionCorrelationId = this.connectionCorrelationId;\n  \u002F\u002F ...server metadata\n});\n```\n\n**Use `connectionCorrelationId` when**: an operation emits multiple independent events that must be joined for analysis (especially fire-and-forget sub-events).\n\n### Session-scoped correlation — ongoing sessions\n\nFor features where a user session produces many events (e.g., shell, REPL, interactive query editing), generate a session-scoped correlation ID and attach it to every event in that session. This enables aggregation (total commands per session, session duration as max-min timestamps):\n\n```typescript\nconst sessionId = randomUUID();\n\n\u002F\u002F Each action within the session carries the same ID\ncontext.telemetry.properties.shellSessionId = sessionId;\ncontext.telemetry.measurements.commandIndex = commandCount++;\n```\n\n## Controlling Telemetry Behavior\n\n### Suppressing noisy events\n\n```typescript\n\u002F\u002F High-frequency events (keystroke handlers, document change listeners)\ncontext.telemetry.suppressIfSuccessful = true; \u002F\u002F only emit on error\u002Fcancel\n\n\u002F\u002F Fully silent (internal bookkeeping)\ncontext.telemetry.suppressAll = true;\n```\n\n### Error handling control\n\n```typescript\n\u002F\u002F Don't show error notification to user (background\u002Finternal operations)\ncontext.errorHandling.suppressDisplay = true;\n\n\u002F\u002F Re-throw so the parent caller sees the error\ncontext.errorHandling.rethrow = true;\n\n\u002F\u002F Don't offer \"Report Issue\" button\ncontext.errorHandling.suppressReportIssue = true;\n```\n\n### Activation events\n\nMark startup\u002Finitialization telemetry so it can be filtered out of active-usage analysis:\n\n```typescript\ncontext.telemetry.properties.isActivationEvent = 'true';\n```\n\n## Common Mistakes to Avoid\n\n### ❌ Don't measure duration manually — the framework does it\n\nThe framework auto-captures `measurements.duration` for every `callWithTelemetryAndErrorHandling` event. Never add `Date.now()` tracking for the same purpose:\n\n```typescript\n\u002F\u002F ❌ Wrong — redundant manual timing\nconst startTime = Date.now();\nawait callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  await doWork();\n  context.telemetry.measurements.durationMs = Date.now() - startTime; \u002F\u002F duplicates framework\n});\n\n\u002F\u002F ✅ Correct — framework captures duration automatically\nawait callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  await doWork();\n  \u002F\u002F duration is auto-captured — no manual timing needed\n});\n```\n\n**Exception**: sub-durations within a single event (e.g., \"time spent on init vs. time on eval\") are valid because they measure a _phase_, not the total.\n\n### ❌ Don't create separate success\u002Ffailure telemetry blocks\n\nWrap the operation in a single `callWithTelemetryAndErrorHandling`. The framework sets `result=Succeeded` or `result=Failed` automatically:\n\n```typescript\n\u002F\u002F ❌ Wrong — two fire-and-forget blocks, error rethrow inside void is swallowed\ntry {\n  const result = await doWork();\n  void callWithTelemetryAndErrorHandling('myEvent', async (ctx) => {\n    ctx.telemetry.properties.isError = 'false';\n  });\n} catch (error) {\n  void callWithTelemetryAndErrorHandling('myEvent', async (ctx) => {\n    ctx.telemetry.properties.isError = 'true';\n    throw error; \u002F\u002F ⚠️ swallowed — void discards the promise\n  });\n}\n\n\u002F\u002F ✅ Correct — single wrapper, framework handles success\u002Ffailure\nawait callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  context.errorHandling.suppressDisplay = true;\n  context.errorHandling.rethrow = true; \u002F\u002F let caller handle display\n  context.telemetry.properties.someProperty = 'value';\n  const result = await doWork(); \u002F\u002F errors auto-set result=Failed\n  context.telemetry.properties.resultType = result.type;\n});\n```\n\n### ❌ Don't use `suppressIfSuccessful` on events you want to measure\n\n`suppressIfSuccessful = true` means the event is **only emitted on error**. Never use it on events where you need to count successful occurrences:\n\n```typescript\n\u002F\u002F ❌ Wrong — successful completions are silently dropped\nregisterCommand('completionAccepted', (context) => {\n  context.telemetry.properties.category = category;\n  context.telemetry.suppressIfSuccessful = true; \u002F\u002F BUG: won't emit on success\n});\n\n\u002F\u002F ✅ Correct — every acceptance is tracked\nregisterCommand('completionAccepted', (context) => {\n  context.telemetry.properties.category = category;\n  \u002F\u002F no suppress — we want to count every acceptance\n});\n```\n\n**When `suppressIfSuccessful` IS correct**: high-frequency background operations where you only care about failures (e.g., keystroke handlers, document change listeners, periodic health checks).\n\n### ❌ Don't throw errors inside `void callWithTelemetryAndErrorHandling`\n\nWhen using fire-and-forget (`void`) telemetry, thrown errors are swallowed because nobody awaits the promise. The framework captures the error in telemetry properties but the throw has no effect on the calling code:\n\n```typescript\n\u002F\u002F ❌ Wrong — throw inside void is swallowed, no effect on caller\nvoid callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  throw error; \u002F\u002F captured in telemetry but NOT re-thrown to caller\n});\n\u002F\u002F code continues here regardless\n\n\u002F\u002F ✅ Correct for fire-and-forget — just set properties, don't throw\nvoid callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  context.telemetry.properties.shellSessionId = sessionId;\n  \u002F\u002F fire-and-forget: no throw, no await needed\n});\n\n\u002F\u002F ✅ Correct when you need error propagation — use await\nawait callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  context.errorHandling.rethrow = true;\n  await riskyOperation(); \u002F\u002F error propagates to caller via rethrow\n});\n```\n\n## Masking Sensitive Data\n\n**Never let connection strings, passwords, tokens, or hostnames appear in telemetry.**\n\n```typescript\nimport { maskSensitiveValuesInTelemetry } from '..\u002F..\u002Fdocumentdb\u002Futils\u002FconnectionStringHelpers';\n\n\u002F\u002F Mask all sensitive parts of a parsed connection string\nmaskSensitiveValuesInTelemetry(context, parsedConnectionString);\n\n\u002F\u002F Or mask individual values\ncontext.valuesToMask.push(password, token);\n```\n\n## Tree View Events\n\nTree `getChildren` calls emit telemetry. Always set context so analytics can distinguish navigation levels:\n\n```typescript\nreturn callWithTelemetryAndErrorHandling('getChildren', async (context) => {\n  context.telemetry.properties.parentNodeContext = element ? (await element.getTreeItem()).contextValue : 'root';\n  context.telemetry.measurements.childrenCount = children.length;\n  return children;\n});\n```\n\n**Note**: Root-level `getChildren` fires automatically when panels become visible (passive rendering). Analytics filters these with `parentNodeContext != \"root\"` — always set this property.\n\n## What Data Points to Capture — Decision Guide\n\nWhen instrumenting a feature, consider these categories:\n\n### For any operation\n\n- **What variant\u002Fmode** was used (property) — enables segmentation\n- **How many items** were involved (measurement) — reveals usage scale\n- **Server\u002Fenvironment type** when relevant (property) — different backends behave differently\n\n### For slow or async operations\n\n- Sub-step durations (measurements) — total duration is auto-captured, but breakdowns show where time goes\n- Retry\u002Fattempt counts (measurement) — reveals flakiness\n- Timeout vs success (property) — important for polling patterns\n\n### For operations that return results\n\n- **Requested vs returned count** — reveals empty results, pagination patterns, limit hits\n- **Result classification** — category\u002Ftype breakdown of what was returned\n\n### For multi-step flows\n\n- **Correlation ID** — link events into a journey\n- **Step identification** — which step succeeded\u002Ffailed (sub-step events or `lastStep` property)\n- **Cancellation point** — where the user abandoned the flow\n\n### For session-based features (shell, REPL, interactive editors)\n\n- **Session correlation ID** — group all events from one session\n- **Command\u002Faction index** — sequence number within the session (enables count-per-session aggregation since there's typically no \"session end\" event)\n- **Server type** — what kind of server the session targets\n\n### For error recovery flows\n\n- **Recovery success flag** — did the user successfully recover (e.g., `reconnected = 'true'`)\n- **Error-then-recovery correlation** — link the original error to the recovery action\n\n## When to Override `result` Manually\n\nThe wrapper auto-sets `result` to `\"Succeeded\"` \u002F `\"Failed\"` \u002F `\"Canceled\"`. Manual override is **required** in two cases:\n\n1. **Early return on invalid state** — function returns without throwing, but the operation logically failed:\n\n```typescript\nif (!isValidFormat) {\n  context.telemetry.properties.result = 'Failed';\n  context.telemetry.properties.errorReason = 'invalidNodeIdFormat';\n  return; \u002F\u002F no throw → wrapper would report \"Succeeded\" without the override\n}\n```\n\n2. **Non-throwing error signals** — e.g., tRPC returns `{ ok: false }` instead of throwing:\n\n```typescript\nif (!result.ok) {\n  context.telemetry.properties.result = 'Failed';\n  context.telemetry.properties.error = result.error.name;\n}\n```\n\n**Do NOT** override `result` when you also `throw` — the wrapper handles that automatically.\n\n## Common Pitfalls\n\n- **Don't measure duration** — `callWithTelemetryAndErrorHandling` does this already\n- **Don't set `result` when throwing** — the wrapper sets `\"Succeeded\"` \u002F `\"Failed\"` \u002F `\"Canceled\"` automatically from the throw. Only override `result` on early-return failures or non-throwing error paths (see above)\n- **Don't catch errors just for telemetry** — the wrapper catches and records them\n- **Don't log sensitive data** — use `maskSensitiveValuesInTelemetry` or `context.valuesToMask`\n- **Don't create a new `callWithTelemetryAndErrorHandling` for every minor step** — only when you need separate duration\u002Fresult tracking for a step\n- **Don't forget `context.errorHandling.rethrow = true`** in sub-steps that should propagate failures to the parent\n- **Don't use `suppressIfSuccessful`** on events you need to count — it drops successful events entirely\n- **Don't store numbers as string properties** — use `measurements` for anything you'd want to aggregate (P50, sum, avg). Use `.toString()` only for numbers used as categorical grouping keys\n- **Don't use `.toString()` for booleans** — use the explicit ternary: `value ? 'true' : 'false'`\n","# Telemetry Instrumentation\n\nHow to instrument code in vscode-documentdb to produce actionable telemetry.\n\n## What the Framework Gives You for Free\n\nThe `@microsoft\u002Fvscode-azext-utils` library **automatically captures** these — never add them manually:\n\n| Auto-captured           | Details                                                            |\n| ----------------------- | ------------------------------------------------------------------ |\n| **Duration**            | `measurements.duration` — wall-clock ms from callback start to end |\n| **Result**              | `properties.result` — `\"Succeeded\"`, `\"Failed\"`, or `\"Canceled\"`   |\n| **Error info**          | `properties.error` (name), `properties.errorMessage` (message)     |\n| **Cancel detection**    | `UserCancelledError` sets result to `\"Canceled\"` automatically     |\n| **Machine\u002Fsession IDs** | `VSCodeMachineId`, `VSCodeSessionId` — always present              |\n| **Extension metadata**  | Extension name, version, VS Code version                           |\n\n**Do not** re-measure duration, set `result`, or catch errors just for telemetry — the wrapper handles it.\n\n## Core API\n\n### `callWithTelemetryAndErrorHandling(eventId, callback)`\n\nThe primary wrapper. Creates a telemetry event, runs the callback, reports result + duration + errors. The callback's return value is passed through, so you can wrap any function call directly:\n\n```typescript\nimport { callWithTelemetryAndErrorHandling } from '@microsoft\u002Fvscode-azext-utils';\n\n\u002F\u002F Simple — wrap logic, telemetry is automatic\nawait callWithTelemetryAndErrorHandling('myFeature.doSomething', async (context) => {\n  context.telemetry.properties.someProperty = 'value';\n  context.telemetry.measurements.itemCount = items.length;\n  \u002F\u002F ... your logic — errors are caught and reported automatically\n});\n\n\u002F\u002F With return value — no need for intermediate variables\nconst result = await callWithTelemetryAndErrorHandling('myFeature.query', async (context) => {\n  context.telemetry.properties.queryType = 'find';\n  return doWork(); \u002F\u002F return value passes through to the caller\n});\n```\n\n**Event name convention**: use dot-separated hierarchy: `connect`, `connect.getmetadata`, `connect.promptForCredentials`.\n\n### Command Registration (auto-telemetry)\n\nEvery registered command gets a telemetry event automatically. Prefer the correlation-wrapped variants:\n\n```typescript\nimport { registerCommandWithTreeNodeUnwrapping } from '@microsoft\u002Fvscode-azext-utils';\nimport { withTreeNodeCommandCorrelation, withCommandCorrelation } from '..\u002F..\u002Futils\u002FcommandTelemetry';\n\n\u002F\u002F Tree node commands — unwraps tree item + tracks journeyCorrelationId\nregisterCommandWithTreeNodeUnwrapping(\n  'vscode-documentdb.command.connectionsView.myCommand',\n  withTreeNodeCommandCorrelation(myCommandHandler),\n);\n\n\u002F\u002F Plain commands — tracks journeyCorrelationId\nregisterCommand('vscode-documentdb.command.myCommand', withCommandCorrelation(myCommandHandler));\n```\n\nFor critical commands where errors need modal dialogs:\n\n```typescript\nimport { registerCommandWithTreeNodeUnwrappingAndModalErrors } from '..\u002F..\u002Futils\u002FcommandErrorHandling';\n```\n\n### tRPC Procedures (auto-telemetry)\n\nWebview tRPC calls get telemetry automatically via `publicProcedureWithTelemetry`. Each call emits `documentDB.rpc.{type}.{path}` with result, error, abort tracking. The DocumentDB `TelemetryRunner` contributes the full `IActionContext` to `ctx.actionContext` (narrow `ctx` to `WithTelemetry\u003CRouterContext>` to read it):\n\n```typescript\nmyProcedure: publicProcedureWithTelemetry.input(schema).query(async ({ ctx, input }) => {\n    const myCtx = ctx as WithTelemetry\u003CRouterContext>;\n    myCtx.actionContext.telemetry.properties.someDetail = 'value';\n    myCtx.actionContext.telemetry.measurements.resultCount = results.length;\n    return results;\n}),\n```\n\n## Naming Conventions\n\n### Property & measurement names\n\n- **Default**: use **camelCase** — `authMethod`, `connectionMode`, `discoveryProviderId`\n- **Namespaced metadata**: when spreading a flat metadata object into telemetry (e.g., cluster metadata collected once and reused), use **prefix_camelCase** — `domainInfo_isAzure`, `serverInfo_version`. The prefix acts as a namespace. This pattern exists in `getClusterMetadata.ts` and is matched by dashboard queries.\n- **Never mix** within one prefix: `task_initializationCompleted` (mixed snake + camelCase) is wrong — use `task_initializationCompleted` → `taskInitializationCompleted` or `task_initialization_completed` consistently.\n- **Correlation IDs**: always use camelCase — `journeyCorrelationId`, `connectionCorrelationId`.\n\n### Boolean properties\n\n**Always use the ternary pattern** — it makes the intent explicit:\n\n```typescript\n\u002F\u002F ✅ Correct — clear intent, consistent\ncontext.telemetry.properties.hasFolders = totalFolders > 0 ? 'true' : 'false';\ncontext.telemetry.properties.isFirstConnection = isFirst ? 'true' : 'false';\n\n\u002F\u002F ❌ Avoid — works but less readable, can accidentally stringify non-booleans\ncontext.telemetry.properties.isWindows = isWindows.toString();\n```\n\n### Numbers: property or measurement?\n\n**Rule: if you'd ever want to compute P50, average, sum, or max over it, use a measurement.** Numbers stored as string properties can't be aggregated in analytics without casting.\n\n```typescript\n\u002F\u002F ✅ Measurement — enables P50(pageNumber), avg(itemCount)\ncontext.telemetry.measurements.pageNumber = input.pageNumber;\ncontext.telemetry.measurements.itemCount = items.length;\n\n\u002F\u002F ❌ Wrong — forces dashboard to todouble(Properties.pageNumber)\ncontext.telemetry.properties.pageNumber = input.pageNumber.toString();\n```\n\n**Exception**: numeric values used purely as categories (e.g., an error code used for grouping, not aggregation) can be properties.\n\n## Properties vs Measurements\n\n| Use                       | Type                                 | Example                                          |\n| ------------------------- | ------------------------------------ | ------------------------------------------------ |\n| **Properties** (string)   | Categorical data, flags, identifiers | `serverType`, `view`, `authMethod`, `hasFolders` |\n| **Measurements** (number) | Counts, sizes, durations, rates      | `itemCount`, `loadTimeMs`, `retryAttempts`       |\n\n### Properties — what to capture\n\n```typescript\n\u002F\u002F Feature context — what variant\u002Fmode is being used\ncontext.telemetry.properties.view = 'connectionsView';\ncontext.telemetry.properties.experience = node.experience.api; \u002F\u002F 'vCore' | 'RU'\ncontext.telemetry.properties.authMethod = 'connectionString';\n\n\u002F\u002F Boolean flags — always use 'true'\u002F'false' strings\ncontext.telemetry.properties.hasFolders = totalFolders > 0 ? 'true' : 'false';\ncontext.telemetry.properties.isFirstConnection = isFirst ? 'true' : 'false';\n\n\u002F\u002F Classification of inputs or results\ncontext.telemetry.properties.connectionMode = 'documentdb'; \u002F\u002F what was selected\ncontext.telemetry.properties.terminalType = 'PowerShell'; \u002F\u002F what environment\n\n\u002F\u002F Error classification beyond the auto-captured error\ncontext.telemetry.properties.connectionErrorType = 'auth'; \u002F\u002F error category\ncontext.telemetry.properties.parseError = 'SyntaxError'; \u002F\u002F specific error type\n\n\u002F\u002F View identification — set for user-initiated or view-specific operations\n\u002F\u002F Skip for background tasks, activation events, or cross-cutting concerns\ncontext.telemetry.properties.view = Views.ConnectionsView;\n```\n\n### Measurements — what to capture\n\n```typescript\n\u002F\u002F Counts — how many items were involved\ncontext.telemetry.measurements.itemCount = results.length;\ncontext.telemetry.measurements.totalConnections = connections.length;\ncontext.telemetry.measurements.savedConnections = rootItems.length;\n\n\u002F\u002F Requested vs returned — reveals pagination, limits, empty results\ncontext.telemetry.measurements.pageSize = input.pageSize;\ncontext.telemetry.measurements.documentCount = docs.length;\n\n\u002F\u002F Sub-durations — where time is spent (the total is auto-captured)\ncontext.telemetry.measurements.loadTimeMs = Date.now() - startTime;\ncontext.telemetry.measurements.mainFileLoad = (loadEnd - loadStart) \u002F 1000;\n\n\u002F\u002F Attempts\u002Fretries\ncontext.telemetry.measurements.connectionViewActivationAttempts = attempt + 1;\ncontext.telemetry.measurements.cleanupIterations = iteration;\n\n\u002F\u002F Breakdown counts — for multi-category results\ncontext.telemetry.measurements.createRecommendationCount = createCount;\ncontext.telemetry.measurements.dropRecommendationCount = dropCount;\n\n\u002F\u002F Structural complexity — depth, nesting\ncontext.telemetry.measurements.maxFolderDepth = maxDepth;\n```\n\n### Dynamic property\u002Fmeasurement names\n\nWhen the set of categories is data-driven, use a prefix:\n\n```typescript\ncontext.telemetry.measurements[`${zonePrefix}_Connections`] = connectionsInZone;\ncontext.telemetry.measurements[`${zonePrefix}_Folders`] = foldersInZone;\n```\n\n## Sub-step Telemetry\n\nUse nested `callWithTelemetryAndErrorHandling` inside a parent event to get fine-grained timing and success\u002Ffailure per step:\n\n```typescript\nawait callWithTelemetryAndErrorHandling('connect', async (context) => {\n  context.telemetry.properties.view = 'discoveryView';\n\n  \u002F\u002F Sub-step: prompting — gets its own duration, result, errors\n  await callWithTelemetryAndErrorHandling('connect.promptForCredentials', async (subCtx) => {\n    subCtx.errorHandling.rethrow = true; \u002F\u002F let parent see failures\n    await wizard.prompt();\n  });\n\n  \u002F\u002F Sub-step: metadata collection (fire-and-forget, non-blocking)\n  void callWithTelemetryAndErrorHandling('connect.getmetadata', async (metaCtx) => {\n    const metadata = await collectMetadata();\n    metaCtx.telemetry.properties = { ...metaCtx.telemetry.properties, ...metadata };\n  });\n});\n```\n\n**When to use sub-steps**:\n\n- Operation has distinct phases (prompt → connect → collect metadata)\n- You need per-phase duration\u002Fsuccess data\n- A sub-step can fail independently without failing the parent\n\n**Naming**: use parent event as prefix: `connect.promptForCredentials`, `connect.getmetadata`, `connect.staticmetadata`.\n\n## Correlation IDs — Linking Related Events\n\n### journeyCorrelationId — user journey across commands\n\nLinks a chain of user actions that form a logical journey (e.g., discover → select → connect → browse). Generated once at the start and propagated through tree items:\n\n```typescript\n\u002F\u002F Generate at the journey start (e.g., when a discovery tree root is created)\nthis.journeyCorrelationId = randomUUID();\n\n\u002F\u002F Tree items carry it to children\nnew MongoRUResourceItem(this, account, this.journeyCorrelationId);\n\n\u002F\u002F Commands pick it up automatically via withTreeNodeCommandCorrelation\u002FwithCommandCorrelation\n\u002F\u002F OR manually in a callWithTelemetryAndErrorHandling:\nif (this.journeyCorrelationId) {\n  context.telemetry.properties.journeyCorrelationId = this.journeyCorrelationId;\n}\n```\n\n**Use `journeyCorrelationId` when**: a user flow spans multiple command invocations that should be analyzed together (discovery → connect → browse, folder operations in sequence).\n\n### connectionCorrelationId — linking connection sub-events\n\nLinks the `connect`, `connect.staticmetadata`, and `connect.getmetadata` events for a single connection attempt:\n\n```typescript\nthis.connectionCorrelationId = randomUUID();\n\nvoid callWithTelemetryAndErrorHandling('connect.staticmetadata', async (context) => {\n  context.telemetry.properties.connectionCorrelationId = this.connectionCorrelationId;\n  \u002F\u002F ...domain metadata\n});\n\nvoid callWithTelemetryAndErrorHandling('connect.getmetadata', async (context) => {\n  context.telemetry.properties.connectionCorrelationId = this.connectionCorrelationId;\n  \u002F\u002F ...server metadata\n});\n```\n\n**Use `connectionCorrelationId` when**: an operation emits multiple independent events that must be joined for analysis (especially fire-and-forget sub-events).\n\n### Session-scoped correlation — ongoing sessions\n\nFor features where a user session produces many events (e.g., shell, REPL, interactive query editing), generate a session-scoped correlation ID and attach it to every event in that session. This enables aggregation (total commands per session, session duration as max-min timestamps):\n\n```typescript\nconst sessionId = randomUUID();\n\n\u002F\u002F Each action within the session carries the same ID\ncontext.telemetry.properties.shellSessionId = sessionId;\ncontext.telemetry.measurements.commandIndex = commandCount++;\n```\n\n## Controlling Telemetry Behavior\n\n### Suppressing noisy events\n\n```typescript\n\u002F\u002F High-frequency events (keystroke handlers, document change listeners)\ncontext.telemetry.suppressIfSuccessful = true; \u002F\u002F only emit on error\u002Fcancel\n\n\u002F\u002F Fully silent (internal bookkeeping)\ncontext.telemetry.suppressAll = true;\n```\n\n### Error handling control\n\n```typescript\n\u002F\u002F Don't show error notification to user (background\u002Finternal operations)\ncontext.errorHandling.suppressDisplay = true;\n\n\u002F\u002F Re-throw so the parent caller sees the error\ncontext.errorHandling.rethrow = true;\n\n\u002F\u002F Don't offer \"Report Issue\" button\ncontext.errorHandling.suppressReportIssue = true;\n```\n\n### Activation events\n\nMark startup\u002Finitialization telemetry so it can be filtered out of active-usage analysis:\n\n```typescript\ncontext.telemetry.properties.isActivationEvent = 'true';\n```\n\n## Common Mistakes to Avoid\n\n### ❌ Don't measure duration manually — the framework does it\n\nThe framework auto-captures `measurements.duration` for every `callWithTelemetryAndErrorHandling` event. Never add `Date.now()` tracking for the same purpose:\n\n```typescript\n\u002F\u002F ❌ Wrong — redundant manual timing\nconst startTime = Date.now();\nawait callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  await doWork();\n  context.telemetry.measurements.durationMs = Date.now() - startTime; \u002F\u002F duplicates framework\n});\n\n\u002F\u002F ✅ Correct — framework captures duration automatically\nawait callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  await doWork();\n  \u002F\u002F duration is auto-captured — no manual timing needed\n});\n```\n\n**Exception**: sub-durations within a single event (e.g., \"time spent on init vs. time on eval\") are valid because they measure a _phase_, not the total.\n\n### ❌ Don't create separate success\u002Ffailure telemetry blocks\n\nWrap the operation in a single `callWithTelemetryAndErrorHandling`. The framework sets `result=Succeeded` or `result=Failed` automatically:\n\n```typescript\n\u002F\u002F ❌ Wrong — two fire-and-forget blocks, error rethrow inside void is swallowed\ntry {\n  const result = await doWork();\n  void callWithTelemetryAndErrorHandling('myEvent', async (ctx) => {\n    ctx.telemetry.properties.isError = 'false';\n  });\n} catch (error) {\n  void callWithTelemetryAndErrorHandling('myEvent', async (ctx) => {\n    ctx.telemetry.properties.isError = 'true';\n    throw error; \u002F\u002F ⚠️ swallowed — void discards the promise\n  });\n}\n\n\u002F\u002F ✅ Correct — single wrapper, framework handles success\u002Ffailure\nawait callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  context.errorHandling.suppressDisplay = true;\n  context.errorHandling.rethrow = true; \u002F\u002F let caller handle display\n  context.telemetry.properties.someProperty = 'value';\n  const result = await doWork(); \u002F\u002F errors auto-set result=Failed\n  context.telemetry.properties.resultType = result.type;\n});\n```\n\n### ❌ Don't use `suppressIfSuccessful` on events you want to measure\n\n`suppressIfSuccessful = true` means the event is **only emitted on error**. Never use it on events where you need to count successful occurrences:\n\n```typescript\n\u002F\u002F ❌ Wrong — successful completions are silently dropped\nregisterCommand('completionAccepted', (context) => {\n  context.telemetry.properties.category = category;\n  context.telemetry.suppressIfSuccessful = true; \u002F\u002F BUG: won't emit on success\n});\n\n\u002F\u002F ✅ Correct — every acceptance is tracked\nregisterCommand('completionAccepted', (context) => {\n  context.telemetry.properties.category = category;\n  \u002F\u002F no suppress — we want to count every acceptance\n});\n```\n\n**When `suppressIfSuccessful` IS correct**: high-frequency background operations where you only care about failures (e.g., keystroke handlers, document change listeners, periodic health checks).\n\n### ❌ Don't throw errors inside `void callWithTelemetryAndErrorHandling`\n\nWhen using fire-and-forget (`void`) telemetry, thrown errors are swallowed because nobody awaits the promise. The framework captures the error in telemetry properties but the throw has no effect on the calling code:\n\n```typescript\n\u002F\u002F ❌ Wrong — throw inside void is swallowed, no effect on caller\nvoid callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  throw error; \u002F\u002F captured in telemetry but NOT re-thrown to caller\n});\n\u002F\u002F code continues here regardless\n\n\u002F\u002F ✅ Correct for fire-and-forget — just set properties, don't throw\nvoid callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  context.telemetry.properties.shellSessionId = sessionId;\n  \u002F\u002F fire-and-forget: no throw, no await needed\n});\n\n\u002F\u002F ✅ Correct when you need error propagation — use await\nawait callWithTelemetryAndErrorHandling('myEvent', async (context) => {\n  context.errorHandling.rethrow = true;\n  await riskyOperation(); \u002F\u002F error propagates to caller via rethrow\n});\n```\n\n## Masking Sensitive Data\n\n**Never let connection strings, passwords, tokens, or hostnames appear in telemetry.**\n\n```typescript\nimport { maskSensitiveValuesInTelemetry } from '..\u002F..\u002Fdocumentdb\u002Futils\u002FconnectionStringHelpers';\n\n\u002F\u002F Mask all sensitive parts of a parsed connection string\nmaskSensitiveValuesInTelemetry(context, parsedConnectionString);\n\n\u002F\u002F Or mask individual values\ncontext.valuesToMask.push(password, token);\n```\n\n## Tree View Events\n\nTree `getChildren` calls emit telemetry. Always set context so analytics can distinguish navigation levels:\n\n```typescript\nreturn callWithTelemetryAndErrorHandling('getChildren', async (context) => {\n  context.telemetry.properties.parentNodeContext = element ? (await element.getTreeItem()).contextValue : 'root';\n  context.telemetry.measurements.childrenCount = children.length;\n  return children;\n});\n```\n\n**Note**: Root-level `getChildren` fires automatically when panels become visible (passive rendering). Analytics filters these with `parentNodeContext != \"root\"` — always set this property.\n\n## What Data Points to Capture — Decision Guide\n\nWhen instrumenting a feature, consider these categories:\n\n### For any operation\n\n- **What variant\u002Fmode** was used (property) — enables segmentation\n- **How many items** were involved (measurement) — reveals usage scale\n- **Server\u002Fenvironment type** when relevant (property) — different backends behave differently\n\n### For slow or async operations\n\n- Sub-step durations (measurements) — total duration is auto-captured, but breakdowns show where time goes\n- Retry\u002Fattempt counts (measurement) — reveals flakiness\n- Timeout vs success (property) — important for polling patterns\n\n### For operations that return results\n\n- **Requested vs returned count** — reveals empty results, pagination patterns, limit hits\n- **Result classification** — category\u002Ftype breakdown of what was returned\n\n### For multi-step flows\n\n- **Correlation ID** — link events into a journey\n- **Step identification** — which step succeeded\u002Ffailed (sub-step events or `lastStep` property)\n- **Cancellation point** — where the user abandoned the flow\n\n### For session-based features (shell, REPL, interactive editors)\n\n- **Session correlation ID** — group all events from one session\n- **Command\u002Faction index** — sequence number within the session (enables count-per-session aggregation since there's typically no \"session end\" event)\n- **Server type** — what kind of server the session targets\n\n### For error recovery flows\n\n- **Recovery success flag** — did the user successfully recover (e.g., `reconnected = 'true'`)\n- **Error-then-recovery correlation** — link the original error to the recovery action\n\n## When to Override `result` Manually\n\nThe wrapper auto-sets `result` to `\"Succeeded\"` \u002F `\"Failed\"` \u002F `\"Canceled\"`. Manual override is **required** in two cases:\n\n1. **Early return on invalid state** — function returns without throwing, but the operation logically failed:\n\n```typescript\nif (!isValidFormat) {\n  context.telemetry.properties.result = 'Failed';\n  context.telemetry.properties.errorReason = 'invalidNodeIdFormat';\n  return; \u002F\u002F no throw → wrapper would report \"Succeeded\" without the override\n}\n```\n\n2. **Non-throwing error signals** — e.g., tRPC returns `{ ok: false }` instead of throwing:\n\n```typescript\nif (!result.ok) {\n  context.telemetry.properties.result = 'Failed';\n  context.telemetry.properties.error = result.error.name;\n}\n```\n\n**Do NOT** override `result` when you also `throw` — the wrapper handles that automatically.\n\n## Common Pitfalls\n\n- **Don't measure duration** — `callWithTelemetryAndErrorHandling` does this already\n- **Don't set `result` when throwing** — the wrapper sets `\"Succeeded\"` \u002F `\"Failed\"` \u002F `\"Canceled\"` automatically from the throw. Only override `result` on early-return failures or non-throwing error paths (see above)\n- **Don't catch errors just for telemetry** — the wrapper catches and records them\n- **Don't log sensitive data** — use `maskSensitiveValuesInTelemetry` or `context.valuesToMask`\n- **Don't create a new `callWithTelemetryAndErrorHandling` for every minor step** — only when you need separate duration\u002Fresult tracking for a step\n- **Don't forget `context.errorHandling.rethrow = true`** in sub-steps that should propagate failures to the parent\n- **Don't use `suppressIfSuccessful`** on events you need to count — it drops successful events entirely\n- **Don't store numbers as string properties** — use `measurements` for anything you'd want to aggregate (P50, sum, avg). Use `.toString()` only for numbers used as categorical grouping keys\n- **Don't use `.toString()` for booleans** — use the explicit ternary: `value ? 'true' : 'false'`\n",{"name":5,"description":7},"main","e15fa6c12e64ad9483e1211805956043ce91720d",".github\u002Fskills\u002Ftelemetry-instrumentation",23,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-11-11 06:07:34",{"source":8,"skillName":5,"commands":54,"prompt":64,"zipAvailable":65},[55,58,61],{"tool":56,"command":57},"npx","npx skills add microsoft\u002Fvscode-documentdb --skill telemetry-instrumentation",{"tool":59,"command":60},"bunx","bunx skills add microsoft\u002Fvscode-documentdb --skill telemetry-instrumentation",{"tool":62,"command":63},"pnpm","pnpm dlx skills add microsoft\u002Fvscode-documentdb --skill telemetry-instrumentation","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftelemetry-instrumentation\u002FSKILL.md",true,[],[68,107,129,160,184,204],{"skillNo":69,"name":70,"nameZh":6,"description":71,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":84,"publishedAt":39},"01M69BSZH60F9JW3N87ZBSKTW8","writing-release-notes","Generates release notes and changelog entries for the DocumentDB extension for Visual Studio Code, and announces releases on GitHub Discussions. Use when preparing version releases, creating patch update notes, writing changelog entries, documenting new features and fixes, or posting a release or patch release announcement to Discussions. Handles both major\u002Fminor versions (new file) and patch updates (append to existing file).","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fwriting-release-notes\u002FSKILL.md",[74,79],{"categoryNo":75,"categoryName":76,"taxonomyType":77,"taxonomyTypeDesc":78},"01JBSEEDCAT000000000000018","写作与文案","SCENARIO","使用场景",{"categoryNo":80,"categoryName":81,"taxonomyType":82,"taxonomyTypeDesc":83},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[85,86,87,88,89,90,91,92,93,94,95,22,96,97,98,99,100,101,102,103,104,105,106],"notes","release","release notes","writing","writing release notes","github","update","visual studio code","visual studio","code","new","updates","studio","patch","changelog","announcement","release announcement","releases","features","append","posting","fixes",{"skillNo":108,"name":109,"nameZh":6,"description":110,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":113,"publishedAt":39},"01M69BSXPVQWSQVYNT6QK38VN1","webview-trpc-messaging","Implements tRPC-based communication between VS Code extension host and React webviews. Use when creating new webview procedures (queries, mutations, subscriptions), adding a new webview router, wiring up a webview controller, using the tRPC client from React components, applying telemetry middleware (telemetryMiddlewareBody), or supporting AbortSignal-based cancellation in webview operations.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fwebview-trpc-messaging\u002FSKILL.md",[],[114,115,116,19,94,117,118,119,120,121,95,122,35,22,123,124,125,126,127,128],"webview trpc messaging","messaging","trpc","components","react","react components","middleware","up","host","wiring","router","queries","controller","mutations","communication",{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":141,"publishedAt":39},"01M69BSV4V52VBDF3RZ3E6Y61T","ux-pr-review","Prepare and run an agent-assisted UX \u002F workflow review of a pull request. Use when the operator says \"I'm about to work on a UX PR review, please prepare\", \"prepare a UX review for PR","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fux-pr-review\u002FSKILL.md",[135,138],{"categoryNo":136,"categoryName":137,"taxonomyType":77,"taxonomyTypeDesc":78},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":139,"categoryName":140,"taxonomyType":82,"taxonomyTypeDesc":83},"01JBSEEDCAT000000000000032","设计师",[142,143,144,145,146,147,148,149,150,151,152,26,153,35,22,154,155,156,157,158,159],"ux pr review","pr","pr review","review","ux","pull request","prepare","workflow","run","pull","ux review","work on","workflow review","work","please","request","about","operator",{"skillNo":161,"name":162,"nameZh":6,"description":163,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":164,"categories":165,"keywords":166,"publishedAt":39},"01M69BSRV752CSC1NBMFWDCKB5","tree-cluster-architecture","Patterns for cluster nodes and tree data providers in vscode-documentdb. Use when adding or changing a cluster tree item, discovery provider, Connections\u002FAzure\u002FDiscovery tree hierarchy, tree\u002Flist mode, cluster identity, treeId\u002FclusterId lookup, Collection View import\u002Fexport resolution, reveal behavior, credentials, shell actions, or copy connection string support.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Ftree-cluster-architecture\u002FSKILL.md",[],[167,168,27,169,170,171,23,172,173,174,22,175,176,177,178,179,38,180,33,181,182,183],"tree cluster architecture","architecture","cluster","azure","support","identity","actions","mode","collection","hierarchy","credentials","reveal","export","list","Providers","STRING","import",{"skillNo":185,"name":186,"nameZh":6,"description":187,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":188,"categories":189,"keywords":191,"publishedAt":39},"01M69BSMRAT64W6F3F15FVQ9QK","skill-creator","Guide for creating effective GitHub Copilot skills (.github\u002Fskills\u002F) in this repository. Use when creating a new skill, updating an existing skill, or when asked about skill structure, format, or best practices for the vscode-documentdb project.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Fskill-creator\u002FSKILL.md",[190],{"categoryNo":136,"categoryName":137,"taxonomyType":77,"taxonomyTypeDesc":78},[192,193,194,90,195,23,196,26,197,95,35,24,22,198,199,200,158,201,202,203],"skill creator","creator","github copilot","best practices","copilot","project","structure","github copilot skills","copilot skills","format","repository","new skill",{"skillNo":205,"name":206,"nameZh":6,"description":207,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":208,"categories":209,"keywords":210,"publishedAt":39},"01M69BSJZA3VPPP4XMY7G2QA6V","review-external-pr","Triage an external contributor's PR and prepare it for merge. Detects push permission, base branch, draft state, size, and history quality, then picks one of three paths (review-only, direct push to contributor branch, or a `reviews\u002F` staging branch). Use when triaging or reviewing a community PR, deciding whether maintainer changes are needed, choosing a merge strategy (squash\u002Fmerge\u002Frebase), or merging an external contribution.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fvscode-documentdb\u002Fblob\u002Fmain\u002F.github\u002Fskills\u002Freview-external-pr\u002FSKILL.md",[],[143,211,145,212,148,213,214,215,216,217,218,22,219,220,221,222,223,224,225,226,26,227],"review external pr","triage","merge","community","reviews","branch","only","contributor","paths","permission","rebase","direct","push","history","three","squash","strategy"]