Hey 👋
@calm-artist-7206 reviewed
github.com/OpenHands/OpenHands/pull/15015 on the OSS
side and suggested bringing it here for enterprise eyes, since it touches the shared
conversation-metadata service.
What it fixes: the conversation-start webhook (on_conversation_update) reads
conversation metadata before the create commits, gets a stub with
selected_repository=None, and the old merge() then nulls the stored repo. The
repository silently vanishes from a running conversation (UI shows "no repo
connected"). Same race as #14476, but the data-loss symptom the retry-only PRs
(#14579 / #14940) don't cover, since re-merging the stub still writes None.
The fix: git fields are write-once on the webhook path only.
save_app_conversation_info gained preserve_git_fields_on_null (default False, COALESCE
only when set); only the webhook passes True. Explicit update_app_conversation
(repository=null to remove) still clears, exactly as before.
Why it needs your eyes: it changes the base
SQLAppConversationInfoService.save_app_conversation_info, which
SaasSQLAppConversationInfoService inherits and calls via super(). So the
default-clears / webhook-preserves behaviour applies to SaaS saves too. Would
appreciate a sanity check that it plays nicely with the SaaS metadata / user-org
consistency path and the existing injector assertions.
Reproduced on a self-hosted box (~40% of repos nulled before, 0 after). CI green,
36 unit tests including the explicit-clear contract and the insert-race recovery. 🙏