Summary
Retire the local telemetry hook and its owned runtime, and let the copilot-otel-metrics skill be the single supported path for Copilot telemetry capture.
The hook has been repaired four times for cross-platform defects (#2532, #2538, #2540, and #2352), and #2496 is still open against it. Rather than repair it a fifth time or reduce its surface as proposed in #2562, this retires the collection mechanism whose design is the source of both the reliability and the privacy exposure.
The privacy case
This is the primary motivation, not a side effect.
Collection was ambient, not consented. The manifest registered one command against eleven lifecycle events: sessionStart, userPromptSubmitted, userPromptSubmit, preToolUse, postToolUse, subagentStart, subagentStop, sessionEnd, stop, agentStop, and preCompact. Installing the plugin enrolled the developer in collection across every prompt, every tool call, and every subagent dispatch. Consent was a property of installation, not a decision the developer made about their own session data.
The skill inverts this. Nothing is captured until the developer enables an editor setting, and the skill presents the exact diff before writing it. Someone who installs the package and never opts in emits nothing.
Ambient collection wrote a raw input artifact. The hook runtime produced raw-input.jsonl on the local disk. That file recorded session input ahead of any filtering decision, so the privacy boundary sat after collection rather than before it.
The replacement puts the boundary first. examples/otel-collector-local.yaml applies a fail-closed redaction processor across the trace, metric, and log pipelines, with allow_all_keys: false making allowed_keys authoritative and blocked_values running as a second independent pass over whatever survives. An attribute introduced by a future extension release is dropped by default rather than stored by default.
Filtering is measured, not asserted. tests/test_collector_carriers.py starts the pinned Collector with the shipped configuration, places a distinct marker in every OTLP carrier the signal model can transport, and records what actually survives. A carrier that opens fails the test rather than passing quietly. The privacy claim is backed by evidence rather than by design intent.
The residual exposure is documented rather than papered over. SECURITY.md is a full STRIDE model across seven trust boundaries with sixteen registered gaps. G-INF-1 states plainly that spans carry prompt text, tool call arguments and results, and system instructions on a configuration left at its documented default, and that the skill cannot change extension behavior. The local path drops unknown content attributes at the Collector and the Azure path deletes the seven observed content attributes before export, but the plaintext loopback hop is named as still outside the skill's control. Honest disclosure of what the control does not reach is worth more than a claim of complete coverage.
Telemetry was in the critical path. Hooks are fail-closed, so a telemetry defect could deny every tool call in a session, which is exactly what #2496 reports. Ancillary observability code held the power to halt all work. Removing it from the hook path removes that class of failure entirely, and it also removes the incentive to weaken the fail-closed posture to accommodate telemetry.
Relationship to open issues
The trade-off #2562 raises still applies and is accepted here. Some report surfaces become dependent on what the developer chose to enable, which is the intended consequence of moving from ambient to opt-in collection.
Capability changes
Two capabilities are removed rather than replaced, and the change should be reviewed with that in mind:
- Automatic hook-driven collection. Capture is now opt-in through an editor setting.
- Local JSONL to static HTML reporting. Reporting moves to Grafana dashboards and query helpers.
Fleet and organization capture through the Azure path is new capability with no hook equivalent.
Scope
- Remove
.github/hooks/shared/telemetry.json and its owned runtime and test package.
- Remove the dedicated local telemetry guide, with
docs/customization/copilot-otel-metrics.md as the replacement.
- Drop the
hooks key and its maturity entry from the affected package recipes.
- Register
copilot-otel-metrics in the experimental package.
- Generalize plugin and extension test fixtures that referenced the real manifest to synthetic fixtures.
Acceptance criteria
Summary
Retire the local telemetry hook and its owned runtime, and let the
copilot-otel-metricsskill be the single supported path for Copilot telemetry capture.The hook has been repaired four times for cross-platform defects (#2532, #2538, #2540, and #2352), and #2496 is still open against it. Rather than repair it a fifth time or reduce its surface as proposed in #2562, this retires the collection mechanism whose design is the source of both the reliability and the privacy exposure.
The privacy case
This is the primary motivation, not a side effect.
Collection was ambient, not consented. The manifest registered one command against eleven lifecycle events:
sessionStart,userPromptSubmitted,userPromptSubmit,preToolUse,postToolUse,subagentStart,subagentStop,sessionEnd,stop,agentStop, andpreCompact. Installing the plugin enrolled the developer in collection across every prompt, every tool call, and every subagent dispatch. Consent was a property of installation, not a decision the developer made about their own session data.The skill inverts this. Nothing is captured until the developer enables an editor setting, and the skill presents the exact diff before writing it. Someone who installs the package and never opts in emits nothing.
Ambient collection wrote a raw input artifact. The hook runtime produced
raw-input.jsonlon the local disk. That file recorded session input ahead of any filtering decision, so the privacy boundary sat after collection rather than before it.The replacement puts the boundary first.
examples/otel-collector-local.yamlapplies a fail-closedredactionprocessor across the trace, metric, and log pipelines, withallow_all_keys: falsemakingallowed_keysauthoritative andblocked_valuesrunning as a second independent pass over whatever survives. An attribute introduced by a future extension release is dropped by default rather than stored by default.Filtering is measured, not asserted.
tests/test_collector_carriers.pystarts the pinned Collector with the shipped configuration, places a distinct marker in every OTLP carrier the signal model can transport, and records what actually survives. A carrier that opens fails the test rather than passing quietly. The privacy claim is backed by evidence rather than by design intent.The residual exposure is documented rather than papered over.
SECURITY.mdis a full STRIDE model across seven trust boundaries with sixteen registered gaps.G-INF-1states plainly that spans carry prompt text, tool call arguments and results, and system instructions on a configuration left at its documented default, and that the skill cannot change extension behavior. The local path drops unknown content attributes at the Collector and the Azure path deletes the seven observed content attributes before export, but the plaintext loopback hop is named as still outside the skill's control. Honest disclosure of what the control does not reach is worth more than a claim of complete coverage.Telemetry was in the critical path. Hooks are fail-closed, so a telemetry defect could deny every tool call in a session, which is exactly what #2496 reports. Ancillary observability code held the power to halt all work. Removing it from the hook path removes that class of failure entirely, and it also removes the incentive to weaken the fail-closed posture to accommodate telemetry.
Relationship to open issues
sessionStartas the opt-in consent gate. This goes further and removes the hook entirely, because the editor setting is a better consent gate than a hook: it is owned by the developer, visible in their own settings, and revocable without touching the package.The trade-off #2562 raises still applies and is accepted here. Some report surfaces become dependent on what the developer chose to enable, which is the intended consequence of moving from ambient to opt-in collection.
Capability changes
Two capabilities are removed rather than replaced, and the change should be reviewed with that in mind:
Fleet and organization capture through the Azure path is new capability with no hook equivalent.
Scope
.github/hooks/shared/telemetry.jsonand its owned runtime and test package.docs/customization/copilot-otel-metrics.mdas the replacement.hookskey and its maturity entry from the affected package recipes.copilot-otel-metricsin the experimental package.Acceptance criteria
npm run lint:hooks,npm run plugin:validate,npm run validate:skills, andnpm run docs:generate:checkpass.pyproject.toml,uv.lock, and its test package so dependency scanning can resolve it.