An XSLT 3.0 solution that uses the parse-xml() function:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<!--standard identity template-->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="description">
<xsl:copy>
<!--Concatenate encoded <p> element to ensure that it is well-formed
XML with a document element when parsed.
Use parse-xml() to parse the encoded markup as a parsed document.
Apply-templates to the parsed document-->
<xsl:apply-templates select="parse-xml(concat('<p>', ., '</p>'))"/>
</xsl:copy>
</xsl:template>
<!-- remove <p> and <b> elements -->
<xsl:template match="p | b">
<xsl:apply-templates/>
</xsl:template>
<!--for every <p> element that has a <b> element, generate a <br/> -->
<xsl:template match="p[b]">
<br/>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>