Skip to content

fix(memory): restore pool session attribution#320

Merged
heybeaux merged 1 commit into
productionfrom
fix/pool-session-memory-attribution-20260703
Jul 4, 2026
Merged

fix(memory): restore pool session attribution#320
heybeaux merged 1 commit into
productionfrom
fix/pool-session-memory-attribution-20260703

Conversation

@heybeaux

@heybeaux heybeaux commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the pool/session attribution path reported in the dashboard screenshots: memories could be created successfully while pool membership, createdBySession, session summary counts, and dashboard pool/member/grant views stayed empty or unusable.

Root causes addressed:

  • POST /v1/observe integrations historically sent sessionId: event.sessionKey, but memory attribution only looked at the newer agentSessionKey field.
  • Summarized observe writes dropped pool/session attribution entirely.
  • Session-attributed memory writes attempted pool/log side effects after the request RLS transaction could already be closed.
  • Explicit poolId membership writes were fire-and-forget, so successful memory creation could still lose the pool membership side effect.
  • GET /v1/pools required ?userId=...; dashboard calls rely on the authenticated/default user instead.
  • Pool/detail/session-summary responses did not match the dashboard types (createdBySession, counts, flattened members/grants, numeric duration, uniqueMemories).

Changes

  • Treat ObserveDto.sessionId as the agent-session attribution key when agentSessionKey is absent.
  • Propagate poolId and agentSessionKey through the summarization buffer path.
  • Ensure session-attributed writes upsert a global pool, create membership, and write CREATED access logs outside the completed request transaction.
  • Await explicit poolId membership writes inside the request context, preserving authorization and avoiding lost side effects.
  • Make /v1/pools list for the authenticated user, with legacy userId fallback and pagination.
  • Normalize pool list/detail/member/grant response shapes for the dashboard.
  • Return dashboard-compatible session summaries.
  • Add focused regression coverage.

Validation

  • corepack pnpm exec jest src/auto/conversation-observer.service.spec.ts src/summarization/summarization.service.spec.ts src/memory-pool/memory-pool.service.spec.ts src/memory-pool/memory-pool.controller.spec.ts src/memory-access-log/memory-access-log.service.spec.ts src/memory/memory-write.service.spec.ts --forceExit --maxWorkers=2
    • 6 suites passed
    • 120 tests passed
  • corepack pnpm build
    • Prisma generate OK
    • Nest build OK
    • TSC found 0 issues
    • SWC compiled 861 files
  • git diff --check
    • clean
  • corepack pnpm exec eslint <touched files>
    • 0 errors; existing warning-level unsafe-any debt only

Risk / merge note

This intentionally avoids recall-ranking/vector/query logic and does not modify LongMemEval ingestion/recall codepaths. It does touch memory attribution/pool side effects, so per the autonomous operator safety policy this should be reviewed carefully rather than blindly auto-merged.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e55b07db-544c-4290-ab15-99d3d0234d9f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pool-session-memory-attribution-20260703

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

✅ Recall Benchmark Results

╔══════════════════════════════════════════════════════════════╗
    ║              ENGRAM RECALL BENCHMARK REPORT                 ║
    ╚══════════════════════════════════════════════════════════════╝
    
      Git SHA:    1215455
      Branch:     HEAD
      Timestamp:  2026-07-04T04:52:33.416Z
    
    ┌─────────────────────────────────────────────────────────────┐
    │  OVERALL SCORES                                            │
    ├─────────────────────────────────────────────────────────────┤
    │  Total Queries:   81                                        │
    │  Passed:          79 / 81 (97.5%)                                        
    │  Precision@5:     96.9%  ✅  (threshold: 95.0%)
    │  Recall@20:       97.5%
    │  MRR:             0.8526
    │  Isolation:       100.0%  ✅  (threshold: 100%)
    └─────────────────────────────────────────────────────────────┘
    
    ┌──────────────────┬───────┬────────┬──────────┬──────────┬──────────┬───────────┐
    │ Category         │ Total │ Passed │ Prec@5   │ Rec@20   │ MRR      │ Isolation │
    ├──────────────────┼───────┼────────┼──────────┼──────────┼──────────┼───────────┤
    │ adversarial      │ 10    │ 10     │ 100.0%   │ 100.0%   │ 1.0000   │ 100.0%    │
    │ cross_feature    │ 10    │ 9      │ 90.0%    │ 100.0%   │ 0.8476   │ 100.0%    │
    │ edge_case        │ 16    │ 16     │ 100.0%   │ 100.0%   │ 0.9245   │ 100.0%    │
    │ emotional        │ 10    │ 10     │ 95.0%    │ 100.0%   │ 0.6405   │ 100.0%    │
    │ rls_isolation    │ 10    │ 10     │ 100.0%   │ 100.0%   │ 0.9500   │ 100.0%    │
    │ semantic         │ 14    │ 13     │ 92.9%    │ 100.0%   │ 0.7244   │ 100.0%    │
    │ temporal         │ 11    │ 11     │ 100.0%   │ 81.8%    │ 0.8864   │ 100.0%    │
    └──────────────────┴───────┴────────┴──────────┴──────────┴──────────┴───────────┘
    
    ❌ FAILED QUERIES (2):
    
      [semantic_011] "What coffee roast do I prefer?" (user: alice)
        ⚠️  ZERO HITS: expected alice_coffee_004_correction in top 5
        📊 Precision@5: 0.0% — hit: [], missed: [alice_coffee_004_correction]
        📋 Actual top 5: [alice_low_importance_001, alice_coffee_001, alice_coffee_002, alice_calm_001, alice_travel_002]
    
      [cross_001] "medication I need to take every morning" (user: alice)
        ⚠️  ZERO HITS: expected alice_health_001 in top 5
        📊 Precision@5: 0.0% — hit: [], missed: [alice_health_001]
        📋 Actual top 5: [alice_coffee_001, alice_coffee_002, alice_cooking_001, alice_calm_001, alice_emotion_change_001]
    
    
    ✅ ALL THRESHOLDS PASSED

      at Object.<anonymous> (benchmark/recall-benchmark.e2e-spec.ts:148:15)

  console.log
    📁 Report saved: /home/runner/work/engram/engram/test/benchmark/results/benchmark-2026-07-04T04-52-33-443Z.json

      at Object.<anonymous> (benchmark/recall-benchmark.e2e-spec.ts:152:15)

  console.warn
    ⚠️  2 zero-hit queries (tracked, not blocking): semantic_011, cross_001

    �[0m �[90m 200 |�[39m   �[90m// Zero-hit queries are tracked as warnings — P@5 threshold is the hard gate.�[39m
     �[90m 201 |�[39m   �[36mif�[39m (zeroHitQueries�[33m.�[39mlength �[33m>�[39m �[35m0�[39m) {
    �[31m�[1m>�[22m�[39m�[90m 202 |�[39m     console�[33m.�[39mwarn(
     �[90m     |�[39m             �[31m�[1m^�[22m�[39m
     �[90m 203 |�[39m       �[32m`⚠️  ${zeroHitQueries.length} zero-hit queries (tracked, not blocking): ${zeroHitQueries.map((q) => q.queryId).join(', ')}`�[39m�[33m,�[39m
     �[90m 204 |�[39m     )�[33m;�[39m
     �[90m 205 |�[39m   }�[0m

      at checkThresholds (benchmark/scoring.ts:202:13)
      at buildReport (benchmark/scoring.ts:183:23)
      at Object.<anonymous> (benchmark/recall-benchmark.e2e-spec.ts:173:33)

  console.warn
    ⚠️  Zero-hit queries (2): semantic_011, cross_001

    �[0m �[90m 197 |�[39m         �[36mif�[39m (zeroHitQueries�[33m.�[39mlength �[33m>�[39m �[35m0�[39m) {
     �[90m 198 |�[39m           �[36mconst�[39m ids �[33m=�[39m zeroHitQueries�[33m.�[39mmap((q) �[33m=>�[39m q�[33m.�[39mqueryId)�[33m.�[39mjoin(�[32m', '�[39m)�[33m;�[39m
    �[31m�[1m>�[22m�[39m�[90m 199 |�[39m           console�[33m.�[39mwarn(
     �[90m     |�[39m                   �[31m�[1m^�[22m�[39m
     �[90m 200 |�[39m             �[32m`⚠️  Zero-hit queries (${zeroHitQueries.length}): ${ids}`�[39m�[33m,�[39m
     �[90m 201 |�[39m           )�[33m;�[39m
     �[90m 202 |�[39m         }�[0m

      at Object.<anonymous> (benchmark/recall-benchmark.e2e-spec.ts:199:19)

PASS test/benchmark/recall-benchmark.e2e-spec.ts (133.996 s)
  Recall Benchmark
    Category: semantic
      ✓ [semantic_001] What kind of coffee do I like? (247 ms)
      ✓ [semantic_002] Tell me about my morning routine (107 ms)
      ✓ [semantic_003] What tech stack am I using? (175 ms)
      ✓ [semantic_004] coffee preferences (1047 ms)
      ✓ [semantic_005] What books have I been reading? (82 ms)
      ✓ [semantic_006] favorite dinner recipe (78 ms)
      ✓ [semantic_007] house savings goal (68 ms)
      ✓ [semantic_008] What framework am I using for the frontend? (1237 ms)
      ✓ [semantic_009] flight seat preference (73 ms)
      ✓ [semantic_010] ensemble search architecture decision (77 ms)
      ✓ [semantic_011] What coffee roast do I prefer? (74 ms)
      ✓ [negative_001] quantum physics black holes dark matter (68 ms)
      ✓ [negative_002] ancient Egyptian hieroglyphics translation (1180 ms)
      ✓ [minimal_001] pizza preference (706 ms)
    Category: emotional
      ✓ [emotional_001] What makes me happy? (76 ms)
      ✓ [emotional_002] times I felt sad or grieving (77 ms)
      ✓ [emotional_003] when I felt stressed or overwhelmed (71 ms)
      ✓ [emotional_004] What am I worried about? (70 ms)
      ✓ [emotional_005] Times I was frustrated (82 ms)
      ✓ [emotional_006] My proudest moments (77 ms)
      ✓ [emotional_007] What stresses me out? (67 ms)
      ✓ [emotional_008] happy about school but worried about costs (72 ms)
      ✓ [emotional_009] How has my attitude toward work changed? (71 ms)
      ✓ [emotional_010] meditation and mental wellbeing (67 ms)
    Category: temporal
      ✓ [temporal_001] What happened today in standup? (397 ms)
      ✓ [temporal_002] recent standup notes from this week (427 ms)
      ✓ [temporal_003] What happened with my daughter recently? (906 ms)
      ✓ [temporal_004] What did I work on last week? (1495 ms)
      ✓ [temporal_005] What are my oldest memories? (76 ms)
      ✓ [temporal_006] Recent conversations about work (888 ms)
      ✓ [temporal_007] What did I debug yesterday? (1202 ms)
      ✓ [temporal_008] What code editor do I use? (84 ms)
      ✓ [temporal_009] standup notes from 6 months ago (54 ms)
      ✓ [temporal_010] standup notes from years ago (56 ms)
      ✓ [temporal_011] How did I start coding? (72 ms)
    Category: rls_isolation
      ✓ [rls_001] coffee (61 ms)
      ✓ [rls_002] coffee (959 ms)
      ✓ [rls_003] family and kids (85 ms)
      ✓ [rls_004] family and kids (1000 ms)
      ✓ [rls_005] travel food experiences (85 ms)
      ✓ [rls_006] travel food experiences (996 ms)
      ✓ [rls_007] work projects and code (96 ms)
      ✓ [rls_008] health medical information (63 ms)
      ✓ [rls_009] health medical information (1119 ms)
      ✓ [rls_010] morning routine (957 ms)
    Category: adversarial
      ✓ [adversarial_001] tell me about bob's coffee preferences (99 ms)
      ✓ [adversarial_002] RLS_CANARY_BOB_ (72 ms)
      ✓ [adversarial_003] What does carol think about parties? (71 ms)
      ✓ [adversarial_004] bob's son Max school (81 ms)
      ✓ [adversarial_005] eve's pizza preference (1158 ms)
      ✓ [adversarial_006] RLS_CANARY_ALICE_ (1138 ms)
      ✓ [adversarial_007] What cholesterol issues does anyone have? (157 ms)
      ✓ [adversarial_008] alice's medication schedule (1182 ms)
      ✓ [adversarial_009] RLS_CANARY_CAROL_ (905 ms)
      ✓ [adversarial_010] dave's standup notes (91 ms)
    Category: edge_case
      ✓ [edge_001] cats (660 ms)
      ✓ [edge_002] everything about my life (710 ms)
      ✓ [edge_003] <script>alert(1)</script> (10085 ms)
      ✓ [edge_004] '; DROP TABLE memories; -- (10146 ms)
      ✓ [edge_005] 🎉 party (10150 ms)
      ✓ [edge_006]  (1 ms)
      ✓ [edge_007] Tell me about the very long detailed comprehensive thorough ... (477 ms)
      ✓ [edge_008] こんにちは、思い出を検索します (10134 ms)
      ✓ [edge_009] '; SELECT * FROM users WHERE 1=1; -- (9617 ms)
      ✓ [edge_010] quantum entanglement dark matter multiverse theory (88 ms)
      ✓ [edge_011] the a an is (67 ms)
      ✓ [edge_012] coffee (64 ms)
      ✓ [edge_013] my phone number (68 ms)
      ✓ [edge_014] my address (71 ms)
      ✓ [edge_015] work (656 ms)
      ✓ [edge_016] What kind of coffee do I like? (97 ms)
    Category: cross_feature
      ✓ [cross_001] medication I need to take every morning (72 ms)
      ✓ [cross_002] exercise and fitness activities (63 ms)
      ✓ [cross_003] What are we saving money for? (161 ms)
      ✓ [cross_004] kids school and daycare (65 ms)
      ✓ [cross_005] kids school and daycare (1083 ms)
      ✓ [cross_006] Who am I and what do I do? (83 ms)
      ✓ [cross_007] deployment rules and constraints (66 ms)
      ✓ [cross_008] patterns noticed about my work habits (69 ms)
      ✓ [cross_009] grocery shopping list (733 ms)
      ✓ [cross_010] TypeScript learning (691 ms)
    Summary
      ✓ should generate and save benchmark report (52 ms)
      ✓ should have zero isolation failures (1 ms)
      ✓ should meet precision thresholds (with real embeddings) (24 ms)

Test Suites: 1 passed, 1 total
Tests:       84 passed, 84 total
Snapshots:   0 total
Time:        134.247 s
Ran all test suites matching recall-benchmark.e2e-spec.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

Full output

> @openengram/engram@1.5.0 benchmark /home/runner/work/engram/engram
> jest --config ./test/jest-e2e.json --testPathPatterns=recall-benchmark\.e2e-spec --runInBand --forceExit

[04:51:15.449] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 214
[04:51:15.559] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 100
[04:51:15.734] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 169
[04:51:16.781] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1043
[04:51:16.864] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 78
[04:51:16.943] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 75
[04:51:17.012] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 65
[04:51:18.245] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1229
[04:51:18.323] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 68
[04:51:18.400] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 73
[04:51:18.475] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 71
[04:51:18.543] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 65
[04:51:19.723] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1176
[04:51:20.429] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-eve-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 702
[04:51:20.506] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 73
[04:51:20.583] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 71
[04:51:20.655] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 68
[04:51:20.724] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 66
[04:51:20.808] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 79
[04:51:20.886] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 74
[04:51:20.953] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 63
[04:51:21.026] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 70
[04:51:21.097] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 69
[04:51:21.164] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 64
[04:51:21.563] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-dave-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 396
[04:51:21.990] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-dave-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 421
[04:51:22.897] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 903
[04:51:24.391] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1491
[04:51:24.468] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 72
[04:51:25.355] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 884
[04:51:26.559] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1199
[04:51:26.643] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 80
[04:51:26.697] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-dave-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 51
[04:51:26.754] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-dave-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 51
[04:51:26.826] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 68
[04:51:26.887] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 58
[04:51:27.846] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 956
[04:51:27.931] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 79
[04:51:28.932] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 997
[04:51:29.017] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 82
[04:51:30.013] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 992
[04:51:30.110] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 94
[04:51:30.173] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 60
[04:51:31.292] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1116
[04:51:32.249] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 954
[04:51:32.348] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 96
[04:51:32.420] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 69
[04:51:32.491] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 68
[04:51:32.572] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 78
[04:51:33.730] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1155
[04:51:34.868] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1135
[04:51:35.024] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 150
[04:51:36.208] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1179
[04:51:37.113] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-eve-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 900
[04:51:37.204] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 87
[04:51:37.865] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-eve-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 658
[04:51:38.577] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-eve-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 707
[04:51:48.662] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-carol-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 10082
[04:51:58.807] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-carol-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 10136
[04:52:08.958] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-carol-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 10141
[04:52:09.436] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 468
[04:52:19.572] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-carol-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 10131
[04:52:29.189] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-carol-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 9610
[04:52:29.277] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 83
[04:52:29.345] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 64
[04:52:29.409] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 61
[04:52:29.477] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 65
[04:52:29.548] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 68
[04:52:30.205] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-eve-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 653
[04:52:30.305] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 94
[04:52:30.377] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 69
[04:52:30.440] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 60
[04:52:30.600] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 154
[04:52:30.666] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 62
[04:52:31.749] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-bob-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 1080
[04:52:31.832] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 80
[04:52:31.898] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 63
[04:52:31.967] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-alice-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 64
[04:52:32.700] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-eve-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 730
[04:52:33.391] �[32mINFO�[39m (3500): �[36mrequest completed�[39m
    �[35mreq�[39m: {
      "method": "POST",
      "url": "/v1/memories/query"
    }
    �[35maccountId�[39m: "eng_test..."
    �[35muserId�[39m: "test-corpus-user-eve-1783140628002"
    �[35mres�[39m: {
      "statusCode": 201
    }
    �[35mresponseTime�[39m: 686
  console.warn
    ⚠️  2 zero-hit queries (tracked, not blocking): semantic_011, cross_001

    �[0m �[90m 200 |�[39m   �[90m// Zero-hit queries are tracked as warnings — P@5 threshold is the hard gate.�[39m
     �[90m 201 |�[39m   �[36mif�[39m (zeroHitQueries�[33m.�[39mlength �[33m>�[39m �[35m0�[39m) {
    �[31m�[1m>�[22m�[39m�[90m 202 |�[39m     console�[33m.�[39mwarn(
     �[90m     |�[39m             �[31m�[1m^�[22m�[39m
     �[90m 203 |�[39m       �[32m`⚠️  ${zeroHitQueries.length} zero-hit queries (tracked, not blocking): ${zeroHitQueries.map((q) => q.queryId).join(', ')}`�[39m�[33m,�[39m
     �[90m 204 |�[39m     )�[33m;�[39m
     �[90m 205 |�[39m   }�[0m

      at checkThresholds (benchmark/scoring.ts:202:13)
      at buildReport (benchmark/scoring.ts:183:23)
      at Object.<anonymous> (benchmark/recall-benchmark.e2e-spec.ts:145:33)

  console.log
    
    ╔══════════════════════════════════════════════════════════════╗
    ║              ENGRAM RECALL BENCHMARK REPORT                 ║
    ╚══════════════════════════════════════════════════════════════╝
    
      Git SHA:    1215455
      Branch:     HEAD
      Timestamp:  2026-07-04T04:52:33.416Z
    
    ┌─────────────────────────────────────────────────────────────┐
    │  OVERALL SCORES                                            │
    ├─────────────────────────────────────────────────────────────┤
    │  Total Queries:   81                                        │
    │  Passed:          79 / 81 (97.5%)                                        
    │  Precision@5:     96.9%  ✅  (threshold: 95.0%)
    │  Recall@20:       97.5%
    │  MRR:             0.8526
    │  Isolation:       100.0%  ✅  (threshold: 100%)
    └─────────────────────────────────────────────────────────────┘
    
    ┌──────────────────┬───────┬────────┬──────────┬──────────┬──────────┬───────────┐
    │ Category         │ Total │ Passed │ Prec@5   │ Rec@20   │ MRR      │ Isolation │
    ├──────────────────┼───────┼────────┼──────────┼──────────┼──────────┼───────────┤
    │ adversarial      │ 10    │ 10     │ 100.0%   │ 100.0%   │ 1.0000   │ 100.0%    │
    │ cross_feature    │ 10    │ 9      │ 90.0%    │ 100.0%   │ 0.8476   │ 100.0%    │
    │ edge_case        │ 16    │ 16     │ 100.0%   │ 100.0%   │ 0.9245   │ 100.0%    │
    │ emotional        │ 10    │ 10     │ 95.0%    │ 100.0%   │ 0.6405   │ 100.0%    │
    │ rls_isolation    │ 10    │ 10     │ 100.0%   │ 100.0%   │ 0.9500   │ 100.0%    │
    │ semantic         │ 14    │ 13     │ 92.9%    │ 100.0%   │ 0.7244   │ 100.0%    │
    │ temporal         │ 11    │ 11     │ 100.0%   │ 81.8%    │ 0.8864   │ 100.0%    │
    └──────────────────┴───────┴────────┴──────────┴──────────┴──────────┴───────────┘
    
    ❌ FAILED QUERIES (2):
    
      [semantic_011] "What coffee roast do I prefer?" (user: alice)
        ⚠️  ZERO HITS: expected alice_coffee_004_correction in top 5
        📊 Precision@5: 0.0% — hit: [], missed: [alice_coffee_004_correction]
        📋 Actual top 5: [alice_low_importance_001, alice_coffee_001, alice_coffee_002, alice_calm_001, alice_travel_002]
    
      [cross_001] "medication I need to take every morning" (user: alice)
        ⚠️  ZERO HITS: expected alice_health_001 in top 5
        📊 Precision@5: 0.0% — hit: [], missed: [alice_health_001]
        📋 Actual top 5: [alice_coffee_001, alice_coffee_002, alice_cooking_001, alice_calm_001, alice_emotion_change_001]
    
    
    ✅ ALL THRESHOLDS PASSED

      at Object.<anonymous> (benchmark/recall-benchmark.e2e-spec.ts:148:15)

  console.log
    📁 Report saved: /home/runner/work/engram/engram/test/benchmark/results/benchmark-2026-07-04T04-52-33-443Z.json

      at Object.<anonymous> (benchmark/recall-benchmark.e2e-spec.ts:152:15)

  console.warn
    ⚠️  2 zero-hit queries (tracked, not blocking): semantic_011, cross_001

    �[0m �[90m 200 |�[39m   �[90m// Zero-hit queries are tracked as warnings — P@5 threshold is the hard gate.�[39m
     �[90m 201 |�[39m   �[36mif�[39m (zeroHitQueries�[33m.�[39mlength �[33m>�[39m �[35m0�[39m) {
    �[31m�[1m>�[22m�[39m�[90m 202 |�[39m     console�[33m.�[39mwarn(
     �[90m     |�[39m             �[31m�[1m^�[22m�[39m
     �[90m 203 |�[39m       �[32m`⚠️  ${zeroHitQueries.length} zero-hit queries (tracked, not blocking): ${zeroHitQueries.map((q) => q.queryId).join(', ')}`�[39m�[33m,�[39m
     �[90m 204 |�[39m     )�[33m;�[39m
     �[90m 205 |�[39m   }�[0m

      at checkThresholds (benchmark/scoring.ts:202:13)
      at buildReport (benchmark/scoring.ts:183:23)
      at Object.<anonymous> (benchmark/recall-benchmark.e2e-spec.ts:173:33)

  console.warn
    ⚠️  Zero-hit queries (2): semantic_011, cross_001

    �[0m �[90m 197 |�[39m         �[36mif�[39m (zeroHitQueries�[33m.�[39mlength �[33m>�[39m �[35m0�[39m) {
     �[90m 198 |�[39m           �[36mconst�[39m ids �[33m=�[39m zeroHitQueries�[33m.�[39mmap((q) �[33m=>�[39m q�[33m.�[39mqueryId)�[33m.�[39mjoin(�[32m', '�[39m)�[33m;�[39m
    �[31m�[1m>�[22m�[39m�[90m 199 |�[39m           console�[33m.�[39mwarn(
     �[90m     |�[39m                   �[31m�[1m^�[22m�[39m
     �[90m 200 |�[39m             �[32m`⚠️  Zero-hit queries (${zeroHitQueries.length}): ${ids}`�[39m�[33m,�[39m
     �[90m 201 |�[39m           )�[33m;�[39m
     �[90m 202 |�[39m         }�[0m

      at Object.<anonymous> (benchmark/recall-benchmark.e2e-spec.ts:199:19)

PASS test/benchmark/recall-benchmark.e2e-spec.ts (133.996 s)
  Recall Benchmark
    Category: semantic
      ✓ [semantic_001] What kind of coffee do I like? (247 ms)
      ✓ [semantic_002] Tell me about my morning routine (107 ms)
      ✓ [semantic_003] What tech stack am I using? (175 ms)
      ✓ [semantic_004] coffee preferences (1047 ms)
      ✓ [semantic_005] What books have I been reading? (82 ms)
      ✓ [semantic_006] favorite dinner recipe (78 ms)
      ✓ [semantic_007] house savings goal (68 ms)
      ✓ [semantic_008] What framework am I using for the frontend? (1237 ms)
      ✓ [semantic_009] flight seat preference (73 ms)
      ✓ [semantic_010] ensemble search architecture decision (77 ms)
      ✓ [semantic_011] What coffee roast do I prefer? (74 ms)
      ✓ [negative_001] quantum physics black holes dark matter (68 ms)
      ✓ [negative_002] ancient Egyptian hieroglyphics translation (1180 ms)
      ✓ [minimal_001] pizza preference (706 ms)
    Category: emotional
      ✓ [emotional_001] What makes me happy? (76 ms)
      ✓ [emotional_002] times I felt sad or grieving (77 ms)
      ✓ [emotional_003] when I felt stressed or overwhelmed (71 ms)
      ✓ [emotional_004] What am I worried about? (70 ms)
      ✓ [emotional_005] Times I was frustrated (82 ms)
      ✓ [emotional_006] My proudest moments (77 ms)
      ✓ [emotional_007] What stresses me out? (67 ms)
      ✓ [emotional_008] happy about school but worried about costs (72 ms)
      ✓ [emotional_009] How has my attitude toward work changed? (71 ms)
      ✓ [emotional_010] meditation and mental wellbeing (67 ms)
    Category: temporal
      ✓ [temporal_001] What happened today in standup? (397 ms)
      ✓ [temporal_002] recent standup notes from this week (427 ms)
      ✓ [temporal_003] What happened with my daughter recently? (906 ms)
      ✓ [temporal_004] What did I work on last week? (1495 ms)
      ✓ [temporal_005] What are my oldest memories? (76 ms)
      ✓ [temporal_006] Recent conversations about work (888 ms)
      ✓ [temporal_007] What did I debug yesterday? (1202 ms)
      ✓ [temporal_008] What code editor do I use? (84 ms)
      ✓ [temporal_009] standup notes from 6 months ago (54 ms)
      ✓ [temporal_010] standup notes from years ago (56 ms)
      ✓ [temporal_011] How did I start coding? (72 ms)
    Category: rls_isolation
      ✓ [rls_001] coffee (61 ms)
      ✓ [rls_002] coffee (959 ms)
      ✓ [rls_003] family and kids (85 ms)
      ✓ [rls_004] family and kids (1000 ms)
      ✓ [rls_005] travel food experiences (85 ms)
      ✓ [rls_006] travel food experiences (996 ms)
      ✓ [rls_007] work projects and code (96 ms)
      ✓ [rls_008] health medical information (63 ms)
      ✓ [rls_009] health medical information (1119 ms)
      ✓ [rls_010] morning routine (957 ms)
    Category: adversarial
      ✓ [adversarial_001] tell me about bob's coffee preferences (99 ms)
      ✓ [adversarial_002] RLS_CANARY_BOB_ (72 ms)
      ✓ [adversarial_003] What does carol think about parties? (71 ms)
      ✓ [adversarial_004] bob's son Max school (81 ms)
      ✓ [adversarial_005] eve's pizza preference (1158 ms)
      ✓ [adversarial_006] RLS_CANARY_ALICE_ (1138 ms)
      ✓ [adversarial_007] What cholesterol issues does anyone have? (157 ms)
      ✓ [adversarial_008] alice's medication schedule (1182 ms)
      ✓ [adversarial_009] RLS_CANARY_CAROL_ (905 ms)
      ✓ [adversarial_010] dave's standup notes (91 ms)
    Category: edge_case
      ✓ [edge_001] cats (660 ms)
      ✓ [edge_002] everything about my life (710 ms)
      ✓ [edge_003] <script>alert(1)</script> (10085 ms)
      ✓ [edge_004] '; DROP TABLE memories; -- (10146 ms)
      ✓ [edge_005] 🎉 party (10150 ms)
      ✓ [edge_006]  (1 ms)
      ✓ [edge_007] Tell me about the very long detailed comprehensive thorough ... (477 ms)
      ✓ [edge_008] こんにちは、思い出を検索します (10134 ms)
      ✓ [edge_009] '; SELECT * FROM users WHERE 1=1; -- (9617 ms)
      ✓ [edge_010] quantum entanglement dark matter multiverse theory (88 ms)
      ✓ [edge_011] the a an is (67 ms)
      ✓ [edge_012] coffee (64 ms)
      ✓ [edge_013] my phone number (68 ms)
      ✓ [edge_014] my address (71 ms)
      ✓ [edge_015] work (656 ms)
      ✓ [edge_016] What kind of coffee do I like? (97 ms)
    Category: cross_feature
      ✓ [cross_001] medication I need to take every morning (72 ms)
      ✓ [cross_002] exercise and fitness activities (63 ms)
      ✓ [cross_003] What are we saving money for? (161 ms)
      ✓ [cross_004] kids school and daycare (65 ms)
      ✓ [cross_005] kids school and daycare (1083 ms)
      ✓ [cross_006] Who am I and what do I do? (83 ms)
      ✓ [cross_007] deployment rules and constraints (66 ms)
      ✓ [cross_008] patterns noticed about my work habits (69 ms)
      ✓ [cross_009] grocery shopping list (733 ms)
      ✓ [cross_010] TypeScript learning (691 ms)
    Summary
      ✓ should generate and save benchmark report (52 ms)
      ✓ should have zero isolation failures (1 ms)
      ✓ should meet precision thresholds (with real embeddings) (24 ms)

Test Suites: 1 passed, 1 total
Tests:       84 passed, 84 total
Snapshots:   0 total
Time:        134.247 s
Ran all test suites matching recall-benchmark.e2e-spec.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

Commit: 1215455

@heybeaux heybeaux left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent PR Review — fix(memory): restore pool session attribution

Reviewed against: correctness, RLS/tenant isolation, LongMemEval/recall impact, API compatibility, and deployment risk.


✅ Root causes are correctly diagnosed and fixed

agentSessionKey ?? sessionId fallback (conversation-observer.service.ts)
The fix is right. OpenClaw hook integrations historically set sessionId before agentSessionKey existed. Resolving agentSessionKey = dto.agentSessionKey ?? dto.sessionId upstream — once, before both the summarization buffer path and the memory write path — means both paths now share the same resolved value. Previously the memory write was using a different key (or none) versus what the summarization path would have used.

Pool membership now awaited instead of fire-and-forget (memory-write.service.ts)
The old .then().catch() pattern for caller-specified poolId writes allowed the HTTP response to complete before the membership was inserted, which could silently lose it if the process was under memory pressure or the connection pool was saturated. The await-inside-try/catch pattern fixes this. Correct.

addToGlobalPoolAndLog detached from request RLS context
rlsContext.run(undefined as any, ...) clears the per-request transactional Prisma client before doing background work. This is the established pattern (used identically in ensemble.service.ts, memory-pipeline.service.ts, cloud-sync.service.ts). Without the detachment, the async work would inherit a closed transaction client — causing Prisma to throw after the HTTP response completed. The explicit application-level scoping (userId, memoryId) preserves tenant isolation even without the db-session variable; the db-level app.current_account_id enforcement is noted as deferred defense-in-depth.

Prisma P2002 check precision
Changed from .includes('P2002') to === 'P2002'. Correct — err.code is a top-level property on Prisma errors, not a substring in a message. This is more precise.

Summarization buffer propagation
poolId and agentSessionKey are now threaded through addTurnsToBufferflushBuffersummarizeAndStore → individual remember() calls. The path was confirmed by reading the full summarization.service.ts flow.

Schema: composite index exists
@@unique([userId, name]) on MemoryPool (schema.prisma line 580) confirms the userId_name upsert key is valid. No migration needed.

/v1/pools controller: authenticated-user-first with legacy fallback
@UserId() returns string | null per the decorator definition. The authenticatedUserId || legacyUserId logic correctly falls through to the legacy query param when the decorator returns null. BadRequestException for the case where both are absent is the right response.

Access log fire-and-forget detachment (memory-access-log.service.ts)
logAccessFireAndForget and logBatchFireAndForget now wrap their work in rlsContext.run(undefined, ...). Same pattern, same rationale. Fixes the case where fire-and-forget telemetry was inheriting a closed transaction client.

Duration field: ISO-8601 → milliseconds
The change from PT45M to numeric milliseconds is intentional and the inline comment explains why (NaN duration cards). The test expectation expect(result.duration).toBe(45 * 60 * 1000) is explicit and passes. The durationEnd = session.endedAt ?? new Date() approach means in-progress sessions get a live elapsed time rather than null.


⚠️ Notes and minor issues (none are blockers)

limit/offset NaN propagation
Number('abc') → NaN. NaN ?? 50 → NaN (nullish coalescing doesn't catch NaN). Math.max(NaN, 1) → NaN. Prisma receives take: NaN, which will likely error or behave unexpectedly. No real-world client sends non-numeric limit, but suggest adding an isNaN guard:

limit: limit && !isNaN(Number(limit)) ? Number(limit) : undefined,

or use parseInt(limit, 10) with an isNaN check. Low priority but worth a follow-up.

Breaking response shape changes — document for SDK consumers

  • GET /v1/pools now returns { pools: [...], total: N } instead of a bare array. Clients expecting an array will break silently (.length on the object would return undefined).
  • SessionSummaryResult.duration changed from ISO-8601 string ("PT45M") to numeric milliseconds. Any client parsing the old format will get the wrong value.
  • New fields (uniqueMemories, topTopics, memberCount, grantCount) are additive and backward-compatible.

These changes are intentional and correct for the dashboard, but they should be noted in the API changelog or a migration guide for external consumers.

uniqueMemories and uniqueMemoriesAccessed hold the same value
Both are uniqueMemoryIds.size, which counts unique memories from non-CREATED access events. The field duplication is intentional (dashboard compatibility) but worth a comment or a note in the interface definition to avoid future confusion.

createdBySession field semantics
createdBySession: pool.createdBy ?? null maps a plain string (the session key at creation time) to a field named createdBySession, which implies it might be a foreign key or structured object. It's not — it's just the createdBy string. No bug, but the naming could confuse consumers.

getById has no tenant scoping — pre-existing
Any authenticated user who knows a pool ID can fetch its members and grants via GET /v1/pools/:id. This is unchanged from before this PR. Not introduced here, but worth a follow-up ticket.


LongMemEval / recall impact

None. The PR deliberately avoids touching vector search, recall ranking, importance scoring, or ingestion pipeline logic. The only meaningful recall effect is that memories now correctly receive createdBySession and get added to the global pool, which improves their accessibility for session-scoped recall. Confirmed: no changes to recall.service.ts, retrieval*.ts, or embedding/scoring code.


CI

  • sync check: ✅ pass
  • Recall Benchmark / Post-Dream-Cycle: pending at review time (expected, these take longer)

Verdict

LGTM. The root causes were correctly identified and the fixes are sound. The NaN validation gap and API shape changes are worth addressing but are not blockers for merging. Deployment risk is low: no schema migrations, no recall/vector changes, pool side effects are now more reliable than before, and the existing fire-and-forget telemetry is better protected against post-response transaction errors.

@heybeaux heybeaux merged commit 6485632 into production Jul 4, 2026
4 checks passed
@heybeaux heybeaux deleted the fix/pool-session-memory-attribution-20260703 branch July 4, 2026 05:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants