Skip to content

fix(msteams): cap team-group-id cache with pruneMapToMaxSize#101964

Closed
sunlit-deng wants to merge 3 commits into
openclaw:mainfrom
sunlit-deng:fix/msteams-graph-thread-cache-cap
Closed

fix(msteams): cap team-group-id cache with pruneMapToMaxSize#101964
sunlit-deng wants to merge 3 commits into
openclaw:mainfrom
sunlit-deng:fix/msteams-graph-thread-cache-cap

Conversation

@sunlit-deng

@sunlit-deng sunlit-deng commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

The teamGroupIdCache Map in resolveTeamGroupId had TTL-based expiry (10 min) but no size cap. A large Teams workspace with many distinct team IDs could accumulate entries faster than the TTL window, allowing unbounded growth.

Why This Change Was Made

Added a 500-entry cap via pruneMapToMaxSize — the shared SDK helper already used by 11 other files. The cap is applied after each successful cache insert.

User Impact

No change for normal usage. Memory usage bounded at ~500 entries regardless of workspace size.

Evidence

Real-path proof — exercises the full resolveTeamGroupIdfetchfetchGraphJson.set()pruneMapToMaxSize production pipeline with tsx (mocks only globalThis.fetch, everything else is production code):

$ npx tsx proof-live.ts
=== live-proof: resolveTeamGroupId production insert/prune path ===

1. 500th insert via resolveTeamGroupId: cache.size = 500
2.   resolveTeamGroupId("team-499") → groupId = guid-team-499
3. 501st insert via resolveTeamGroupId: cache.size = 500
4.   resolveTeamGroupId("team-500") → groupId = guid-team-500
5.   team-0 evicted: true
6. After 1000 resolveTeamGroupId calls: cache.size = 500
7.   team-999 cached: true
8.   Cache hit team-999: groupId = guid-team-999 (no fetch)

=== PROOF COMPLETE ===
  • 499 entries pre-filled → 500th resolveTeamGroupId goes through production fetchfetchGraphJsonset path
  • 501st call: hits the production pruneMapToMaxSize line at graph-thread.ts:89 → oldest evicted
  • 1000 total calls through resolveTeamGroupId: cache stays at 500
  • Cache-hit returns correct groupId without Graph fetch
proof-live.ts
import { _teamGroupIdCacheForTest, resolveTeamGroupId } from "./extensions/msteams/src/graph-thread";

const CAP = 500;

console.log("=== live-proof: resolveTeamGroupId production insert/prune path ===\n");

// Mock global fetch so resolveTeamGroupId's internal Graph call succeeds.
// The fetch → requestGraph → fetchGraphJson → resolveTeamGroupId pipeline
// exercises the real production insert+prune code at graph-thread.ts:89.
const origFetch = globalThis.fetch as typeof globalThis.fetch;
globalThis.fetch = (async (url: string | URL, _init?: RequestInit) => {
  const urlStr = String(url);
  const teamId = urlStr.split("/teams/")[1]?.split("?")[0] || "unknown";
  return new Response(JSON.stringify({ id: `guid-${teamId}` }), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
}) as typeof globalThis.fetch;

// Pre-fill 499 entries directly (avoids 499 wasteful fetch calls).
// resolveTeamGroupId cache-hit path skips Graph, so these are harmless.
for (let i = 0; i < CAP - 1; i++) {
  _teamGroupIdCacheForTest.set(`team-${i}`, {
    groupId: `guid-team-${i}`,
    expiresAt: Date.now() + 600_000,
  });
}

// 500th call: cache miss → fetchGraphJson → set + prune (full production path)
const r499 = await resolveTeamGroupId("token", "team-499");
console.log(`1. 500th insert via resolveTeamGroupId: cache.size = ${_teamGroupIdCacheForTest.size}`);
console.log(`2.   resolveTeamGroupId("team-499") → groupId = ${r499}`);

// 501st call triggers prune internally — oldest entry (team-0) evicted
const r500 = await resolveTeamGroupId("token", "team-500");
console.log(`3. 501st insert via resolveTeamGroupId: cache.size = ${_teamGroupIdCacheForTest.size}`);
console.log(`4.   resolveTeamGroupId("team-500") → groupId = ${r500}`);
console.log(`5.   team-0 evicted: ${!_teamGroupIdCacheForTest.has("team-0")}`);

// Fill 500 more entries via resolveTeamGroupId — size stays at CAP
for (let i = 501; i < 1000; i++) {
  await resolveTeamGroupId("token", `team-${i}`);
}
console.log(`6. After 1000 resolveTeamGroupId calls: cache.size = ${_teamGroupIdCacheForTest.size}`);
console.log(`7.   team-999 cached: ${_teamGroupIdCacheForTest.has("team-999")}`);

// Cache hit still works — no Graph API call for cached entries
const hit = await resolveTeamGroupId("token", "team-999");
console.log(`8.   Cache hit team-999: groupId = ${hit} (no fetch)`);

console.log(`\n=== PROOF COMPLETE ===`);

Vitest — 27/27 passed including cache cap regression:

$ vitest run --project extension-msteams extensions/msteams/src/graph-thread.test.ts

 ✓ resolveTeamGroupId > caps cache at 500 entries — evicts oldest on overflow
 ... 27 passed (27)

AI-assisted: built with Codex

@openclaw-barnacle openclaw-barnacle Bot added channel: msteams Channel integration: msteams size: XS labels Jul 8, 2026
@clawsweeper

clawsweeper Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 8, 2026, 8:15 AM ET / 12:15 UTC.

Summary
This PR imports the plugin SDK bounded-map helper, caps the MS Teams resolveTeamGroupId cache at 500 entries after successful inserts, and adds a regression test for overflow eviction.

PR surface: Source +3, Tests +24. Total +27 across 2 files.

Reproducibility: yes. Source inspection shows current main stores distinct successful Teams Graph lookups in a TTL-only Map, and the PR test/proof exercises the 501-entry overflow path.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: persistent cache schema: extensions/msteams/src/graph-thread.test.ts, vector/embedding metadata: extensions/msteams/src/graph-thread.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P2] Refresh or rebase against current main and let exact-head checks rerun before merge.

Risk before merge

  • [P1] The branch is behind current main, so maintainers should refresh the branch and rerun exact-head checks before landing.

Maintainer options:

  1. Decide the mitigation before merge
    Land the focused cache cap after a branch refresh confirms exact-head CI and mergeability.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P2] No repair lane is needed because there are no blocking findings; maintainers should review, refresh the branch, and merge if exact-head checks pass.

Security
Cleared: The diff only adds bounded in-memory pruning through an existing SDK helper and a colocated test, with no dependency, workflow, secret, package, or code-execution surface change.

Review details

Best possible solution:

Land the focused cache cap after a branch refresh confirms exact-head CI and mergeability.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main stores distinct successful Teams Graph lookups in a TTL-only Map, and the PR test/proof exercises the 501-entry overflow path.

Is this the best way to solve the issue?

Yes. Pruning immediately after successful cache insertion with the existing SDK helper is the narrow maintainable fix and avoids duplicating local LRU code or adding configuration.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 4bf70be01a21.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes terminal output from a tsx proof that drives resolveTeamGroupId through the production fetch/set/prune path with mocked Graph responses and shows cap, eviction, and cache-hit behavior.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes terminal output from a tsx proof that drives resolveTeamGroupId through the production fetch/set/prune path with mocked Graph responses and shows cap, eviction, and cache-hit behavior.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P2: This is a bounded Microsoft Teams memory-risk fix in the optional thread-context path with limited blast radius.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes terminal output from a tsx proof that drives resolveTeamGroupId through the production fetch/set/prune path with mocked Graph responses and shows cap, eviction, and cache-hit behavior.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes terminal output from a tsx proof that drives resolveTeamGroupId through the production fetch/set/prune path with mocked Graph responses and shows cap, eviction, and cache-hit behavior.
Evidence reviewed

PR surface:

Source +3, Tests +24. Total +27 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 3 0 +3
Tests 1 24 0 +24
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 27 0 +27

What I checked:

Likely related people:

  • sudie-codes: Merged PR history shows this account authored the Teams thread-history feature that introduced graph-thread.ts, resolveTeamGroupId, the TTL-only team-group cache, and adjacent tests. (role: feature originator; confidence: high; commits: 8c852d86f759; files: extensions/msteams/src/graph-thread.ts, extensions/msteams/src/graph-thread.test.ts, extensions/msteams/src/monitor-handler/message-handler.ts)
  • vincentkoc: Current-main blame and PR history show this account carried the Teams graph-thread code through the recent plugin refactor. (role: recent area contributor; confidence: medium; commits: cc083e5f4fe0, e085fa1a3ffd; files: extensions/msteams/src/graph-thread.ts, extensions/msteams/src/graph-thread.test.ts)
  • jacobtomlinson: Merged history shows adjacent Teams thread-history allowlist work in the same inbound thread-context path. (role: adjacent contributor; confidence: medium; commits: 5cca38084074; files: extensions/msteams/src/monitor-handler/message-handler.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (13 earlier review cycles; latest 8 shown)
  • reviewed 2026-07-08T04:14:48.198Z sha 0b00562 :: needs real behavior proof before merge. :: [P2] Reset the cache before the cap regression
  • reviewed 2026-07-08T05:32:57.117Z sha c695b20 :: needs real behavior proof before merge. :: [P1] Restore the SDK import as a top-level import | [P1] Move pruning after the cache insert | [P2] Reset the cache before the cap regression
  • reviewed 2026-07-08T06:43:57.533Z sha ac886fd :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-08T07:18:39.975Z sha 1304d3c :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-08T07:39:23.824Z sha 98f54d9 :: needs real behavior proof before merge. :: [P3] Add braces to the raw Map loop
  • reviewed 2026-07-08T07:48:43.293Z sha b28923c :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-08T09:59:03.533Z sha b28923c :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-08T12:08:42.803Z sha b28923c :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 8, 2026
@sunlit-deng
sunlit-deng force-pushed the fix/msteams-graph-thread-cache-cap branch from ac886fd to 9ea8b96 Compare July 8, 2026 02:44
@sunlit-deng

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@sunlit-deng
sunlit-deng force-pushed the fix/msteams-graph-thread-cache-cap branch from 9ea8b96 to 97b9c8c Compare July 8, 2026 03:24
@sunlit-deng

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@sunlit-deng
sunlit-deng force-pushed the fix/msteams-graph-thread-cache-cap branch from 97b9c8c to 0b00562 Compare July 8, 2026 04:00
@sunlit-deng

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@sunlit-deng
sunlit-deng force-pushed the fix/msteams-graph-thread-cache-cap branch from 0b00562 to 4fc07a4 Compare July 8, 2026 04:21
@sunlit-deng sunlit-deng closed this Jul 8, 2026
@sunlit-deng
sunlit-deng force-pushed the fix/msteams-graph-thread-cache-cap branch from 4fc07a4 to a24f15d Compare July 8, 2026 04:33
@sunlit-deng
sunlit-deng deleted the fix/msteams-graph-thread-cache-cap branch July 8, 2026 05:06
@sunlit-deng
sunlit-deng restored the fix/msteams-graph-thread-cache-cap branch July 8, 2026 05:07
@sunlit-deng sunlit-deng reopened this Jul 8, 2026
@sunlit-deng

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@openclaw-barnacle openclaw-barnacle Bot added channel: line Channel integration: line triage: blank-template Candidate: PR template appears mostly untouched. triage: needs-pr-context Candidate: external PR body lacks required problem context or evidence. labels Jul 8, 2026
…upId

Exercises the real resolveTeamGroupId with 501 unique team IDs, proving
pruneMapToMaxSize caps at 500 and evicts oldest by insertion order.
Negative control confirms raw Map would grow to 501 without the cap.
@clawsweeper clawsweeper Bot removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 8, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 8, 2026
@sunlit-deng

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@sunlit-deng

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

2 similar comments
@sunlit-deng

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@sunlit-deng

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jul 8, 2026
@sunlit-deng

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 8, 2026
@steipete steipete self-assigned this Jul 9, 2026
@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Landed the reviewed fix as #102814 in commit f6b9901.

The original fork branch could not be refreshed safely: GitHub's signed createCommitOnBranch update exceeded/expanded stale ancestry into a very large PR surface. I recreated the exact two-file patch on current main, preserved sunlit-deng as co-author, re-ran focused Testbox proof (31/31), and merged after full exact-head CI succeeded.

Thank you @sunlit-deng for the fix and regression coverage.

@steipete steipete closed this Jul 9, 2026
@sunlit-deng

Copy link
Copy Markdown
Contributor Author

Landed the reviewed fix as #102814 in commit f6b9901.

The original fork branch could not be refreshed safely: GitHub's signed createCommitOnBranch update exceeded/expanded stale ancestry into a very large PR surface. I recreated the exact two-file patch on current main, preserved sunlit-deng as co-author, re-ran focused Testbox proof (31/31), and merged after full exact-head CI succeeded.

Thank you @sunlit-deng for the fix and regression coverage.

Thanks Peter, appreciate you landing this and preserving the co-author credit.

I’ve refreshed my other open PR branches and kept maintainer edits enabled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: msteams Channel integration: msteams P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. triage: blank-template Candidate: PR template appears mostly untouched.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants