Skip to content

bug(tests): mcp-server access-wiring "#2807 refused loop is audited as a refusal" reads the previous test's late fire-and-forget audit row — flaky on dev alone #2952

Closed

Description

@webmixgamer

Summary

src/mcp-server/src/access-wiring.test.ts → #2807: a refused loop start is audited as a refusal, not as a successful call fails intermittently with

the refusal was audited as a success: {"tool":"run_agent_loop","duration_ms":1,"success":true}

The test is reading the previous test's audit row. Nothing in the gate or the audit stamping is wrong — the assertion is anchored on the wrong row.

Context

Mechanism

Test order in the describe block:

  1. an agent key with no edge is refused … — refused
  2. the same key with an edge starts the loop on the sibling — starts a loop (success: true)
  3. a loop on itself needs no edge … — starts a loop (success: true)
  4. #2807: a refused loop start is audited as a refusal … — refused

The audit POST /api/internal/audit is fire-and-forget (the file's own header says so). Test 4 does

const seen = auditRows.length;          // captured BEFORE test 3's audit row has necessarily arrived
…
const row = await auditRowFor("run_agent_loop", seen);   // first run_agent_loop row after `seen`

so when test 3's success: true row lands after seen was captured, auditRowFor returns it and the assertion row.details.success === false fails. Under the full-suite run the process is busier and the late arrival is common; alone, the row is usually in before test 4 starts.

Acceptance Criteria

  • The #2807 subtest anchors on its own call's audit row, not "the first run_agent_loop row after an index" — e.g. each test that triggers an audit awaits its row before finishing (drain), or the finder matches a per-call discriminator (the key label / a request marker) rather than the tool name alone.
  • The full suite (node --import tsx --test src/*.test.ts src/tools/*.test.ts) passes 10 of 10 consecutive runs on dev.
  • The same anchoring is applied to the sibling #2807: deny then allow on ONE session … test if it reads auditRows the same way.

Technical Notes

  • src/mcp-server/src/access-wiring.test.ts — auditRowFor(tool, after) and the four it blocks above; reset() clears loopPosts but not auditRows, which is why an index is used.
  • Cheapest durable fix: make reset() await the previous test's audit row (or clear auditRows after awaiting it), so seen is always a settled index. A discriminator match is more robust if tests ever run concurrently.
  • Trinity-side audit stamping (bug(mcp): a denied chat_with_agent / fan_out call is audited as a successful tool call #2807) is not implicated: the refused call's own row does carry success: false, denied: true — the test just finds an earlier row first.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions