mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Separate local and remote plugin analytics IDs (#29495)
## Why Plugin analytics overloaded `plugin_id`: most events used the Codex `<plugin>@<marketplace>` identity, while remote install events used the backend plugin ID. That makes the same field change meaning across event types and complicates downstream identity resolution. This change makes the contract unambiguous: - `plugin_id`: the local Codex `<plugin>@<marketplace>` identity, when resolved - `remote_plugin_id`: the backend plugin identity, when available For a remote install failure that happens before plugin details resolve, `plugin_id` is `null` and `remote_plugin_id` remains populated. ## What changed All six plugin analytics events use the same identity contract: - `codex_plugin_installed` - `codex_plugin_install_failed` - `codex_plugin_uninstalled` - `codex_plugin_enabled` - `codex_plugin_disabled` - `codex_plugin_used` Remote identity is resolved from the current installed-plugin snapshot first, with persisted install metadata as fallback. The telemetry metadata type keeps local identity optional for failures that occur before remote details are available. The app-server test client's manual analytics smokes now find remote mutation events through `remote_plugin_id` and validate that `plugin_id` remains local. ## Remote uninstall Resolve and capture telemetry metadata before removing the local plugin cache, then emit `codex_plugin_uninstalled` after the backend confirms success. The event is also emitted when backend uninstall succeeds but local cache cleanup reports `CacheRemove`. If a concurrent remote-cache refresh removes the local bundle before telemetry capture, the already-fetched remote plugin detail supplies fallback capability metadata. ## Validation - `just test -p codex-analytics` — 82 passed - `just test -p codex-core-plugins` — 271 passed - `just test -p codex-app-server-test-client` — 5 passed - `just test -p codex-plugin` — 3 passed - `just test -p codex-app-server plugin_install` — 37 passed - `just test -p codex-app-server plugin_uninstall` — 10 passed The production app-server install/uninstall flow was also exercised against `plugins~Plugin_f1b845ac33888191ac156169c58733c2` (`build-ios-apps@openai-curated-remote`), and the plugin's original uninstalled state was restored.
This commit is contained in:
1 parent
c5a9a95ab6
commit
ff50b47dce
15 files changed
+427
-91
No files matched your search
@@ -3027,6 +3027,7 @@ fn plugin_used_event_serializes_expected_shape() {
|
||||
"event_type": "codex_plugin_used",
|
||||
"event_params": {
|
||||
"plugin_id": "sample@test",
|
||||
"remote_plugin_id": null,
|
||||
"plugin_name": "sample",
|
||||
"marketplace_name": "test",
|
||||
"has_skills": true,
|
||||
@@ -3057,6 +3058,7 @@ fn plugin_management_event_serializes_expected_shape() {
|
||||
"event_type": "codex_plugin_installed",
|
||||
"event_params": {
|
||||
"plugin_id": "sample@test",
|
||||
"remote_plugin_id": null,
|
||||
"plugin_name": "sample",
|
||||
"marketplace_name": "test",
|
||||
"has_skills": true,
|
||||
@@ -3086,6 +3088,7 @@ fn plugin_install_failed_event_serializes_expected_shape() {
|
||||
"event_type": "codex_plugin_install_failed",
|
||||
"event_params": {
|
||||
"plugin_id": "sample@test",
|
||||
"remote_plugin_id": null,
|
||||
"plugin_name": "sample",
|
||||
"marketplace_name": "test",
|
||||
"has_skills": true,
|
||||
@@ -3099,7 +3102,7 @@ fn plugin_install_failed_event_serializes_expected_shape() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_management_event_can_use_remote_plugin_id_override() {
|
||||
fn plugin_management_event_keeps_plugin_id_local_when_remote_id_exists() {
|
||||
let mut plugin = sample_plugin_metadata();
|
||||
plugin.remote_plugin_id = Some("plugins~Plugin_remote".to_string());
|
||||
let event = TrackEventRequest::PluginInstalled(CodexPluginEventRequest {
|
||||
@@ -3110,11 +3113,21 @@ fn plugin_management_event_can_use_remote_plugin_id_override() {
|
||||
let payload = serde_json::to_value(&event).expect("serialize plugin installed event");
|
||||
|
||||
assert_eq!(
|
||||
payload["event_params"]["plugin_id"],
|
||||
"plugins~Plugin_remote"
|
||||
payload,
|
||||
json!({
|
||||
"event_type": "codex_plugin_installed",
|
||||
"event_params": {
|
||||
"plugin_id": "sample@test",
|
||||
"remote_plugin_id": "plugins~Plugin_remote",
|
||||
"plugin_name": "sample",
|
||||
"marketplace_name": "test",
|
||||
"has_skills": true,
|
||||
"mcp_server_count": 2,
|
||||
"connector_ids": ["calendar", "drive"],
|
||||
"product_client_id": originator().value
|
||||
}
|
||||
})
|
||||
);
|
||||
assert_eq!(payload["event_params"]["plugin_name"], "sample");
|
||||
assert_eq!(payload["event_params"]["marketplace_name"], "test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -3454,6 +3467,7 @@ async fn reducer_ingests_plugin_state_changed_fact() {
|
||||
"event_type": "codex_plugin_disabled",
|
||||
"event_params": {
|
||||
"plugin_id": "sample@test",
|
||||
"remote_plugin_id": null,
|
||||
"plugin_name": "sample",
|
||||
"marketplace_name": "test",
|
||||
"has_skills": true,
|
||||
@@ -3489,6 +3503,7 @@ async fn reducer_ingests_plugin_install_failed_fact() {
|
||||
"event_type": "codex_plugin_install_failed",
|
||||
"event_params": {
|
||||
"plugin_id": "sample@test",
|
||||
"remote_plugin_id": null,
|
||||
"plugin_name": "sample",
|
||||
"marketplace_name": "test",
|
||||
"has_skills": true,
|
||||
@@ -3506,7 +3521,7 @@ async fn reducer_ingests_plugin_install_failed_fact_without_detail() {
|
||||
let mut reducer = AnalyticsReducer::default();
|
||||
let mut events = Vec::new();
|
||||
let plugin = PluginTelemetryMetadata {
|
||||
plugin_id: PluginId::parse("unknown@openai-curated-remote").expect("valid plugin id"),
|
||||
plugin_id: None,
|
||||
remote_plugin_id: Some("plugins~Plugin_00000000000000000000000000000000".to_string()),
|
||||
capability_summary: None,
|
||||
};
|
||||
@@ -3529,9 +3544,10 @@ async fn reducer_ingests_plugin_install_failed_fact_without_detail() {
|
||||
json!([{
|
||||
"event_type": "codex_plugin_install_failed",
|
||||
"event_params": {
|
||||
"plugin_id": "plugins~Plugin_00000000000000000000000000000000",
|
||||
"plugin_name": "unknown",
|
||||
"marketplace_name": "openai-curated-remote",
|
||||
"plugin_id": null,
|
||||
"remote_plugin_id": "plugins~Plugin_00000000000000000000000000000000",
|
||||
"plugin_name": null,
|
||||
"marketplace_name": null,
|
||||
"has_skills": null,
|
||||
"mcp_server_count": null,
|
||||
"connector_ids": null,
|
||||
@@ -4570,7 +4586,7 @@ async fn turn_completed_without_started_notification_emits_null_started_at() {
|
||||
|
||||
fn sample_plugin_metadata() -> PluginTelemetryMetadata {
|
||||
PluginTelemetryMetadata {
|
||||
plugin_id: PluginId::parse("sample@test").expect("valid plugin id"),
|
||||
plugin_id: Some(PluginId::parse("sample@test").expect("valid plugin id")),
|
||||
remote_plugin_id: None,
|
||||
capability_summary: Some(PluginCapabilitySummary {
|
||||
config_name: "sample@test".to_string(),
|
||||
|
||||
Reference in new issue
Block a user