Skip to content

seam(echidnabot): GraphQL contract mismatch — client queries 3 ops that the server does not expose #180

Description

@hyperpolymath

Diagnosis

Seam-audit 2026-06-01 surfaced a hard contract mismatch on the echidna ↔ echidnabot GraphQL seam. In EchidnaApiMode::Graphql the client is completely broken; in EchidnaApiMode::Auto the failures are silently swallowed and every call degrades to REST after a wasted round-trip + warn-log.

Client expects (echidnabot/src/dispatcher/echidna_client.rs)

Operation Type Signature
verifyProof(prover, content) mutation returns { status, message, proverOutput, durationMs, artifacts }
suggestTactics(prover, context, goalState) mutation returns [{ tactic, confidence, explanation }]
proverStatus(prover) query returns { available, message }

Server exposes (echidna/src/interfaces/graphql/schema.rs)

Operation Type Signature
submitProof(goal, prover) mutation returns ProofState
applyTactic(...) mutation
cancelProof(proofId) mutation
provers query [ProverInfo]
proofState(id) query ProofState?
listProofs(limit, status) query [ProofState]
suggestTactics(proofId, limit) query [Tactic] — note: query not mutation, takes proofId not (prover, context, goalState)
health query String

Zero of the client's three operations exist on the server. suggestTactics is the closest match by name only — different kind (query vs mutation), different arguments (proof-id vs prover/context/goal), different return shape (no confidence/explanation).

REST seam (Seam 1 sub-finding)

Tighter, but also drift: server's /api/verify REST response now carries a typed outcome: String (PROVED / NO_PROOF_FOUND / TIMEOUT / INVALID_INPUT / etc.) plus optional mode and smt_status. Client's RestVerifyResponse deserializes only valid: bool and discards everything else. Recoverable (serde ignores unknown fields), but the client cannot distinguish TIMEOUT from FAILED via REST, and rich diagnostics are dropped.

Suggested resolution paths

Two plausible directions; owner picks:

A. Document one side as canonical, port the other. Either the GraphQL schema in echidna is the SoT (then echidnabot client needs rewriting against submitProof/proofState polling, or we extend the schema with a synchronous verifyProof(prover, content) mutation) or the echidnabot client's contract is the SoT (then echidna's MutationRoot gets the three missing operations).

B. Sunset GraphQL on the echidnabot path. Per the memory entry on echidnabot Phase 6 (BoJ-routed MCP), it's possible the GraphQL client is legacy. Make EchidnaApiMode::Rest the only supported mode and gate / remove GraphQL paths in EchidnaConfig.

Either way, add an integration test that round-trips a contract assertion (e.g. a cargo test in echidnabot CI spawns echidna's GraphQL playground and asserts the three operations exist with the expected argument shapes — would have caught this in either repo).

Don't-touch boundaries respected

  • src/abi/ in echidnabot is owner-intentional broken namespace — not touched.
  • This is a pure diagnosis issue; no code change proposed.

Filed by seam-audit per estate directive (cross-repo Closes doesn't auto-fire; if a follow-up PR lands in echidnabot rather than here, please close this manually).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions