LearnNewsExamplesServices
Frontmatter
id10937
titleG5#3: PermissionService.spec AGENT:* singleton cross-pollution under workers:1
stateClosed
labels
bugaitesting
assigneesneo-gpt
createdAtMay 8, 2026, 12:12 AM
updatedAtMay 12, 2026, 4:09 AM
githubUrlhttps://github.com/neomjs/neo/issues/10937
authorneo-opus-4-7
commentsCount1
parentIssue10924
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 10, 2026, 12:35 AM

G5#3: PermissionService.spec AGENT:* singleton cross-pollution under workers:1

Closedbugaitesting
neo-opus-4-7
neo-opus-4-7 commented on May 8, 2026, 12:12 AM

Context

Surfaced 2026-05-08 during PR #10933 (Phase 3 unit-row re-add) CI run 25524203756. Originally classified as G5#3 in the #10924 G5 triage matrix. Triage hypothesis was "auto-resolve post-G4-merge"; falsified empirically by the unit-row first activation in CI.

Skip-guarded in PR #10933 commit 8e6c3fd78 per the established pattern; this ticket tracks the proper investigation + fix.

The Problem

Test test/playwright/unit/ai/mcp/server/memory-core/services/PermissionService.spec.mjs:165 flakes with:

expect(node.type).toBe('BroadcastSentinel');
  Expected: "BroadcastSentinel"
  Received: undefined

The test pre-seeds AGENT:* as a BroadcastSentinel via GraphService.upsertNode, then asserts the type survives grantPermission. Empirically node.type is undefined — meaning either:

  • A sibling spec re-upserted AGENT:* with a different shape (no type field), OR
  • The singleton GraphService was re-initialized between the seed and the read, dropping the in-memory state

workers:1 substrate amplifies cross-pollution. Per grep, 4 specs touch AGENT:*:

  • test/playwright/unit/ai/mcp/server/memory-core/services/MailboxService.spec.mjs
  • test/playwright/unit/ai/mcp/server/memory-core/services/WriteSideInvariant.spec.mjs
  • test/playwright/unit/ai/mcp/server/memory-core/services/GraphService.spec.mjs
  • test/playwright/unit/ai/mcp/server/memory-core/services/PermissionService.spec.mjs (this one)

Whichever sibling runs first and writes AGENT:* without a type field clobbers the BroadcastSentinel shape.

The Architectural Reality

  • test/playwright/unit/ai/mcp/server/memory-core/services/PermissionService.spec.mjs:165-181 — the flaky test
  • ai/mcp/server/memory-core/services/GraphService.mjs#upsertNode — last-writer-wins on the id key; updates fields per the upsert payload
  • 4 spec-files all write AGENT:* to the same singleton GraphService
  • workers:1 serializes execution → cumulative state pollution

The Fix (TBD via investigation)

Two candidate paths:

  1. Spec-level: each spec uses a UNIQUE AGENT:* analog (e.g., AGENT:*permission-test, AGENT:*mailbox-test) so writes don't collide on the same node ID
  2. Symmetric beforeEach+afterEach: each spec resets AGENT:* to a known-clean shape at the start AND end of each test (per feedback_symmetric_spec_cleanup pattern from earlier session)

Investigation needed to determine which sibling clobbers first + which fix shape minimizes blast radius.

Acceptance Criteria

  • (AC1) Empirically reproduce the flake locally with WORKERS=1
  • (AC2) Identify which sibling spec(s) write AGENT:* without preserving type
  • (AC3) Implement chosen fix path (per-spec namespaced ID OR symmetric reset)
  • (AC4) Remove the NEO_TEST_SKIP_CI guard added in PR #10933 commit 8e6c3fd78
  • (AC5) Verify PermissionService.spec runs deterministically across 5 consecutive npm run test-unit invocations on CI substrate

Out of Scope

  • Refactoring GraphService.upsertNode to require explicit type field (architectural change beyond spec-isolation scope)
  • Migrating GraphService to per-test instances

Avoided Traps

  • Increasing retries: 2 → 5: papers over without addressing root cause
  • Skip-guard as permanent solution: applied as immediate ship-the-PR move + tracked here for proper fix

Related

  • Surfacing CI run: 25524203756
  • Triage origin: #10924 G5 row (G5#3)
  • Triage correction: #10924 comment 4401547656
  • Sibling state-pollution patterns: G5#2 (KBRecorderService — singleton-data), #10934 (FileSystemIngestor — singleton SQLite-close), #10935 (TransportService residual)
  • Substrate config: test/playwright/playwright.config.unit.mjs workers: 1 in CI
  • Skip-guard commit: 8e6c3fd78 on PR #10933

Origin Session ID: 7e897a0b-33ce-4d6c-b1a9-a1ff93e4e571

Retrieval Hint: query_raw_memories(query="PermissionService AGENT:* singleton cross-pollution workers 1 flake G5#3 #10924 PR 10933")

tobiu referenced in commit 98897fc - "feat(ci): re-add unit suite to matrix post-Bucket-G substrate (#10939) (#10953) on May 8, 2026, 2:43 PM
tobiu referenced in commit 2405528 - "feat(ai): migrate memory-core/Server to extend BaseServer + beforeToolDispatch hook (#10965) (#10977) on May 8, 2026, 6:45 PM
tobiu referenced in commit 205925d - "fix(memory-core): stabilize broadcast sentinel permission spec (#10937) (#11063) on May 10, 2026, 12:35 AM
tobiu closed this issue on May 10, 2026, 12:35 AM