fix(msteams): proactive sends fail after conversation migration#104691
fix(msteams): proactive sends fail after conversation migration#104691jimmypuckett wants to merge 1 commit into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed July 11, 2026, 6:53 PM ET / 22:53 UTC. Summary PR surface: Source +26, Tests +119. Total +145 across 3 files. Reproducibility: yes. from source and supplied exact-head evidence. The production-store regression and upgrade transcript establish the failure, retained source, retry, canonical lookup, and archival sequence, although this read-only review did not independently execute the PR. Review metrics: 1 noteworthy metric.
Stored data model Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the canonical-key, archive-after-visibility migration and its production-store regressions on the current base, without adding a steady-state legacy fallback. Do we have a high-confidence way to reproduce the issue? Yes from source and supplied exact-head evidence. The production-store regression and upgrade transcript establish the failure, retained source, retry, canonical lookup, and archival sequence, although this read-only review did not independently execute the PR. Is this the best way to solve the issue? Yes. The patch repairs the migration owner boundary and uses the same normalized identity as proactive runtime lookup, avoiding a new runtime fallback or parallel state source. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 088a9dc33557. Label changesLabel justifications:
Evidence reviewedPR surface: Source +26, Tests +119. Total +145 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
Review history (5 earlier review cycles)
|
798f85e to
9ab0693
Compare
|
Exact-head proof update for
Given the exact-head green CI and the narrowed claim above, please re-review this as the narrower preservation/safety fix rather than a complete canonical proactive-send repair. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
9ab0693 to
ed7b433
Compare
|
Updated at exact head ed7b433 with the real production-store regression ClawSweeper requested. Evidence covered by the new regression in extensions/msteams/doctor-contract-api.test.ts:
Production change: conversation migration now resolves the stored Bot Framework reference conversation.id before falling back to the legacy JSON key, so archived migrations leave rows visible at the same canonical key the Teams proactive send path uses. Validation on this head:
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Exact-head redacted upgrade-path proof for This proof uses a disposable local OpenClaw state home under The fixture is an authentic legacy Redacted proof transcript: {
"head": "ed7b43389155f9f6c0e3919b8079f532797502d9",
"proofMode": "exact PR-head source msteams doctor contract + production plugin-state SQLite context",
"disposableStateDir": "<tmp-proof-root>/state-dir",
"fixture": {
"legacyJsonKey": "legacy-user-key",
"embeddedConversationId": "19:[email protected]",
"retainedConversationEntries": 1
},
"afterFailure": {
"failureCode": "PLUGIN_STATE_LIMIT_EXCEEDED",
"failureMessage": "Plugin state for msteams exceeds the 1 live row limit.",
"legacyExists": true,
"legacySizeBytes": 552,
"archiveExists": false,
"sqliteExists": true
},
"afterRetry": {
"changes": [
"Migrated 1 Microsoft Teams conversation entry -> plugin state",
"Archived Microsoft Teams conversation legacy source -> <tmp-proof-root>/state-dir/msteams-conversations.json.migrated"
],
"warningCount": 0,
"legacyExists": false,
"archiveExists": true,
"archiveSizeBytes": 552,
"sqliteExists": true
},
"canonicalVisibility": {
"canonicalKeyPrefix": "36fb5f4161c2",
"legacyKeyPrefix": "8987278c02e0",
"pluginStateLookupByEmbeddedConversationIdKey": true,
"pluginStateLookupByLegacyJsonKey": false,
"storedConversationId": "19:[email protected]",
"storedUserId": "29:redacted-user"
},
"sqlite": {
"path": "<tmp-proof-root>/state-dir/state/openclaw.sqlite",
"msteamsNamespaceCounts": [
{
"plugin_id": "msteams",
"namespace": "conversations",
"rows": 1
},
{
"plugin_id": "msteams",
"namespace": "proof-sibling",
"rows": 1
}
],
"conversationKeyRows": [
{
"namespace": "conversations",
"keyPrefix": "36fb5f4161c2",
"matchesCanonicalKey": true,
"matchesLegacyJsonKey": false
}
]
}
}What this demonstrates:
Exact-head CI remains green: |
|
@clawsweeper re-review |
|
This pull request has been automatically marked as stale due to inactivity. |
|
Active maintenance update: this Microsoft Teams conversation-migration PR is not abandoned. The published PR head still matches its fork branch, and I am tracking the current integration state. The exact PR ref currently has unrelated history from upstream/main in a local merge-tree check, so I am preserving the published branch rather than force-pushing or synthesizing history. I am continuing to investigate the focused recovery path. |
Closes #94939.
What Problem This Solves
Fixes an issue where Microsoft Teams users could lose proactive Bot Framework sends after 6.x state migration when a non-empty legacy
msteams-conversations.jsonsource produced no conversation references visible in shared plugin state.When that happens, archiving the legacy source silently removes recoverable Teams conversation data. The bot then cannot send proactively to those conversations until it receives a new message.
Why This Change Was Made
The migration now treats a non-empty legacy Teams conversation source with zero visible migrated rows as unsafe to archive. It leaves the legacy source in place and reports a warning/change result instead of silently discarding recoverable conversation references.
The change is intentionally narrow: empty legacy sources and successfully verified migrations keep the existing archive behavior. This does not attempt to reinterpret every possible legacy migration failure.
User Impact
Teams users and operators keep a recoverable legacy conversation reference source when migration cannot verify that references landed in shared plugin state. That reduces the risk of proactive Teams sends breaking after state migration and gives operators a clear warning instead of silent data loss.
Evidence
Regression/source validation:
node scripts/run-vitest.mjs extensions/msteams/doctor-contract-api.test.tsnode scripts/run-vitest.mjs extensions/msteamspnpm lint --threads=8pnpm run lint:extensions:bundledpnpm run lint:extensions:channelsgit diff --checkRedacted local gateway validation:
{ "installedBundle": { "migrationGuardPresentInPatchedBundle": true, "migrationGuardPresentInBaselineBundle": false, "patchedDoctorContractSha256": "bb75fa0ebca072a1393c359968124e36e8895c0126538d51ad44698e4d4ad629", "baselineDoctorContractSha256": "43b77bdaf0da989857df4190b653318e5cdb8d42006a7ef1560f4804c11192ae" }, "runtime": { "gatewayProcess": "running", "msteamsListener": "listening" }, "stateCounters": { "msteamsConversationReferences": 20, "msteamsSentMessageRecordsBeforeProactiveSend": 6, "msteamsSentMessageRecordsAfterProactiveSend": 7 }, "teamsSmoke": { "inboundDmReceived": true, "botReplyObserved": true, "proactiveSendDryRun": false, "sentMessageRecordsIncreasedAfterProactiveSend": true, "latestPersonalConversationTimestampUpdatedAfterProactiveSend": true, "latestPersonalConversationUpdatedAt": "2026-07-02T02:30:21.367Z" }, "privacy": { "conversationIds": "redacted", "tenantAndUserIds": "redacted", "messageBodiesFromLogs": "not included", "localHostnamesAndUsernames": "not included" } }Migration note: This PR replaces #98862 with the same published head commit, moved from
spinen/openclawtojimmypuckett/openclawso OpenClaw maintainers and repository automation can update the contribution without transferring authorship. Prior review, proof, and CI history remain available on #98862.