Skip to content

fix(mcp): spar.check_chain resolves a chain as latency does and reports a nested-spelling flow as invalid - #491

Merged
avrabe merged 1 commit into
mainfrom
fix/mcp-check-chain-descent
Oct 15, 2026
Merged

avrabe merged 1 commit into
mainfrom
fix/mcp-check-chain-descent

Conversation

@avrabe

@avrabe avrabe commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Refs #455. Follow-up to #488, under REQ-MCP-001, aligning MCP spar.check_chain with REQ-ANALYSIS-FLOW-SEGMENT-LEGALITY-001 and REQ-ANALYSIS-FLOW-THREAD-DESCENT-001.

Before

The tool matched a requested thread against any dotted part of a flow's first and last segment. Its fixtures used the nested spelling the analysis now rejects. On app.producer.f1 -> loop_back -> app.consumer.f2, with source producer and sink consumer, it returned Ok. spar analyze errors on that same model, and app or f1 would have matched too.

After

  • Legal flows resolve through spar_analysis::latency::flow_components, the one public entry point onto latency's own descent. The tool and the analysis can't disagree about which components a flow passes.
    • The source must be the first component the flow reaches, and the sink the last.
    • Endpoints are reported as instance paths.
  • Non-matching requests give CHAIN_NOT_FOUND: app, a flow name, a feature name, or reversed endpoints.
  • A nested-spelling flow gives CHAIN_INVALID, carrying flow_check's reason.
  • A valid match always wins over a rejected one, whichever is declared first.

Visible to MCP clients

The endpoints strings are now instance paths, not owner/segment.

Oracles

  • Fixtures were converted to legal AADL. There are six new in-process tests and one flow_components unit test.
  • In-diff mutation: 11 of 18 MISSED before the tests. After: 11 caught, 5 unviable, 0 missed.
  • Workspace: 3291 passed, 0 failed. fmt and clippy are clean.

Implemented by a Sonnet subagent under supervision, and re-verified independently. The tree is byte-identical to the one verified.

Not claimed

To report CHAIN_INVALID, the tool still recognises a nested flow by any dotted part of its end segments. That path only ever produces the rejection, never a resolved chain.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BnBWxgVEUPdsBTLNKtrA6Z

…eaches, as latency resolves them, and reports a nested-spelling flow as invalid

Follow-up to REQ-ANALYSIS-FLOW-SEGMENT-LEGALITY-001 and
REQ-ANALYSIS-FLOW-THREAD-DESCENT-001 (#455 item 3), under REQ-MCP-001.

The tool matched a requested thread against ANY dotted part of a flow's first
and last segment, and its fixtures used the spelling the analysis now
rejects. On `app.producer.f1 -> loop_back -> app.consumer.f2`, with source
`producer` and sink `consumer`, it returned Ok with endpoints like
`Sys/app.producer`, while `spar analyze` on the same model errors. `app` or
`f1` would have matched too.

Now:
- legal flows resolve through `spar_analysis::latency::flow_components`,
  the single public entry point onto latency's own descent, so the tool and
  the analysis cannot disagree about which components a flow passes;
- the source must be the first component reached and the sink the last;
- endpoints are instance paths (`.../app/producer`);
- `app`, a flow name, a feature name or reversed endpoints give
  CHAIN_NOT_FOUND;
- a nested-spelling flow gives CHAIN_INVALID carrying flow_check's reason;
- a valid match always wins over a rejected one, whichever is declared
  first.

`expand_flow_steps` and `FlowStep` stay private.

Fixtures converted to legal AADL: a flow path on the process, implemented
through the threads. Six new in-process tests and a `flow_components` unit
test. MUTATION, in-diff: 11 of 18 MISSED before the new tests; after,
11 caught, 5 unviable, 0 missed. Two redundant empty-needle guards were
removed, because `required_string` already rejects empty input.

Implemented by a Sonnet subagent under supervision. 3291 passed / 0 failed
and the mutation counts were re-derived independently before this commit.

VISIBLE CHANGE to MCP clients: the `endpoints` strings are now instance
paths instead of `owner/segment`.

NOT CLAIMED: to report CHAIN_INVALID, a nested flow is still recognised by
any dotted part of its first and last segment. That path only produces the
rejection and never a resolved chain, so an unrelated nested flow that
mentions both names also reports invalid.

Refs #455

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BnBWxgVEUPdsBTLNKtrA6Z
@github-actions

Copy link
Copy Markdown

Rivet verification gate

✅ 20/20 passed

count
Passed 20
Failed 0
Skipped (no steps) 0

Filter: (and (= type "feature") (or (has-tag "v093") (has-tag "v0100")))

Failed artifacts

(none)

Updated automatically by tools/post_verification_comment.py. Source of truth: artifacts/verification.yaml.

@codecov

codecov Bot commented Oct 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.83721% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/spar-mcp/src/tools/check_chain.rs 98.07% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@avrabe
avrabe merged commit eb09f52 into main Oct 15, 2026
21 checks passed
@avrabe
avrabe deleted the fix/mcp-check-chain-descent branch October 15, 2026 01:20
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.

1 participant