diff --git a/integrations/agent-plugin-core/python/telemetry.py b/integrations/agent-plugin-core/python/telemetry.py index b7e52d24f..b3c2ef586 100644 --- a/integrations/agent-plugin-core/python/telemetry.py +++ b/integrations/agent-plugin-core/python/telemetry.py @@ -370,6 +370,14 @@ def claim_install(was_empty: bool | None = None) -> str | None: }, stream, ) + # Durable before this returns. The O_EXCL open is what makes the + # claim exclusive, so it cannot be replaced by a temp-and-rename + # without losing that, which leaves the content as the thing to make + # safe. A kill between the open and this fsync used to leave a marker + # that exists but parses to nothing: is_first_run reads it as claimed + # and claim_version_change cannot read a version out of it. + stream.flush() + os.fsync(stream.fileno()) except OSError: pass return "upgrade" if upgrading else "install" diff --git a/integrations/antigravity-plugin/core/telemetry.py b/integrations/antigravity-plugin/core/telemetry.py index b7e52d24f..b3c2ef586 100644 --- a/integrations/antigravity-plugin/core/telemetry.py +++ b/integrations/antigravity-plugin/core/telemetry.py @@ -370,6 +370,14 @@ def claim_install(was_empty: bool | None = None) -> str | None: }, stream, ) + # Durable before this returns. The O_EXCL open is what makes the + # claim exclusive, so it cannot be replaced by a temp-and-rename + # without losing that, which leaves the content as the thing to make + # safe. A kill between the open and this fsync used to leave a marker + # that exists but parses to nothing: is_first_run reads it as claimed + # and claim_version_change cannot read a version out of it. + stream.flush() + os.fsync(stream.fileno()) except OSError: pass return "upgrade" if upgrading else "install" diff --git a/integrations/claude-code-plugin/core/telemetry.py b/integrations/claude-code-plugin/core/telemetry.py index b7e52d24f..b3c2ef586 100644 --- a/integrations/claude-code-plugin/core/telemetry.py +++ b/integrations/claude-code-plugin/core/telemetry.py @@ -370,6 +370,14 @@ def claim_install(was_empty: bool | None = None) -> str | None: }, stream, ) + # Durable before this returns. The O_EXCL open is what makes the + # claim exclusive, so it cannot be replaced by a temp-and-rename + # without losing that, which leaves the content as the thing to make + # safe. A kill between the open and this fsync used to leave a marker + # that exists but parses to nothing: is_first_run reads it as claimed + # and claim_version_change cannot read a version out of it. + stream.flush() + os.fsync(stream.fileno()) except OSError: pass return "upgrade" if upgrading else "install" diff --git a/integrations/codex-plugin/core/telemetry.py b/integrations/codex-plugin/core/telemetry.py index b7e52d24f..b3c2ef586 100644 --- a/integrations/codex-plugin/core/telemetry.py +++ b/integrations/codex-plugin/core/telemetry.py @@ -370,6 +370,14 @@ def claim_install(was_empty: bool | None = None) -> str | None: }, stream, ) + # Durable before this returns. The O_EXCL open is what makes the + # claim exclusive, so it cannot be replaced by a temp-and-rename + # without losing that, which leaves the content as the thing to make + # safe. A kill between the open and this fsync used to leave a marker + # that exists but parses to nothing: is_first_run reads it as claimed + # and claim_version_change cannot read a version out of it. + stream.flush() + os.fsync(stream.fileno()) except OSError: pass return "upgrade" if upgrading else "install" diff --git a/integrations/cursor-plugin/core/telemetry.py b/integrations/cursor-plugin/core/telemetry.py index b7e52d24f..b3c2ef586 100644 --- a/integrations/cursor-plugin/core/telemetry.py +++ b/integrations/cursor-plugin/core/telemetry.py @@ -370,6 +370,14 @@ def claim_install(was_empty: bool | None = None) -> str | None: }, stream, ) + # Durable before this returns. The O_EXCL open is what makes the + # claim exclusive, so it cannot be replaced by a temp-and-rename + # without losing that, which leaves the content as the thing to make + # safe. A kill between the open and this fsync used to leave a marker + # that exists but parses to nothing: is_first_run reads it as claimed + # and claim_version_change cannot read a version out of it. + stream.flush() + os.fsync(stream.fileno()) except OSError: pass return "upgrade" if upgrading else "install" diff --git a/integrations/kimi-plugin/core/telemetry.py b/integrations/kimi-plugin/core/telemetry.py index b7e52d24f..b3c2ef586 100644 --- a/integrations/kimi-plugin/core/telemetry.py +++ b/integrations/kimi-plugin/core/telemetry.py @@ -370,6 +370,14 @@ def claim_install(was_empty: bool | None = None) -> str | None: }, stream, ) + # Durable before this returns. The O_EXCL open is what makes the + # claim exclusive, so it cannot be replaced by a temp-and-rename + # without losing that, which leaves the content as the thing to make + # safe. A kill between the open and this fsync used to leave a marker + # that exists but parses to nothing: is_first_run reads it as claimed + # and claim_version_change cannot read a version out of it. + stream.flush() + os.fsync(stream.fileno()) except OSError: pass return "upgrade" if upgrading else "install" diff --git a/integrations/mem0-agent-plugin/core/telemetry.py b/integrations/mem0-agent-plugin/core/telemetry.py index b7e52d24f..b3c2ef586 100644 --- a/integrations/mem0-agent-plugin/core/telemetry.py +++ b/integrations/mem0-agent-plugin/core/telemetry.py @@ -370,6 +370,14 @@ def claim_install(was_empty: bool | None = None) -> str | None: }, stream, ) + # Durable before this returns. The O_EXCL open is what makes the + # claim exclusive, so it cannot be replaced by a temp-and-rename + # without losing that, which leaves the content as the thing to make + # safe. A kill between the open and this fsync used to leave a marker + # that exists but parses to nothing: is_first_run reads it as claimed + # and claim_version_change cannot read a version out of it. + stream.flush() + os.fsync(stream.fileno()) except OSError: pass return "upgrade" if upgrading else "install"