Standardise Issue/Recommendation formatting across all agents

Replace single-line bullet format with a structured two-part format
(**Issue:** / **Recommendation:**) in all specialist and lead agent
prompts. Update Gemini response schema description to match. Update
frontend formatFeedbackText and formatFeedbackTextForPDF to parse
**bold** markdown and preserve line breaks within multi-line bullets.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
michael 2026-02-19 10:17:08 -06:00
parent f1183e1bff
commit 3207ec301c
8 changed files with 129 additions and 47 deletions

View file

@ -143,9 +143,14 @@ If the proof is nonsensical, not a marketing material, or cannot be analyzed, se
**Response Format:**
- Keep feedback brief and scannable
- Use bullet points for each finding
- Each bullet should be one actionable sentence
- Start with the issue, then the recommendation
- Example: "Logo placement incorrect (bottom-left) - move to top-right corner per guidelines"
- Structure each feedback bullet as two clearly labelled parts separated by a line break:
**Issue:** [Clear description of what's wrong]
**Recommendation:** [Actionable fix what to do and how]
- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**)
- Always place the Recommendation on a new line after the Issue (not on the same line)
- Example:
"• **Issue:** The logo is placed in the bottom-left corner, which doesn't align with brand guidelines.
**Recommendation:** Move the logo to the top-right corner as specified in the brand guidelines."
- IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed").
- IMPORTANT: Never use the words "violation", "violates", or "violated" in your output. Use constructive alternatives such as "issue", "doesn't align with", "doesn't meet", or "conflicts with".
- IMPORTANT: Use Plain English throughout. Choose simple, clear words over complex vocabulary. Prefer: "add" over "incorporate/integrate", "about" over "regarding", "qualifies as" over "constitutes", "use" over "utilise", "before" over "prior to", "to" over "in order to", "try" over "endeavour", "then" over "subsequently", "put in place" over "implement", "keep/contain" over "constrain", "standard interest rate" over "reversion rate". Avoid unnecessary jargon (e.g. use "exaggerated claim" instead of "puffery"). Feedback should be easy to understand for all users.

View file

@ -136,9 +136,14 @@ If the proof is nonsensical, not a marketing material, or cannot be analyzed, se
**Response Format:**
- Keep feedback brief and scannable
- Use bullet points for each finding
- Each bullet should be one actionable sentence
- Start with the issue, then the recommendation
- Example: "CTA placement below fold - move above fold for better visibility"
- Structure each feedback bullet as two clearly labelled parts separated by a line break:
**Issue:** [Clear description of what's wrong]
**Recommendation:** [Actionable fix what to do and how]
- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**)
- Always place the Recommendation on a new line after the Issue (not on the same line)
- Example:
"• **Issue:** The Call-to-Action (CTA) is placed below the fold, reducing visibility.
**Recommendation:** Move the CTA above the fold so users see it without scrolling."
- IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed").
- IMPORTANT: Never use the words "violation", "violates", or "violated" in your output. Use constructive alternatives such as "issue", "doesn't align with", "doesn't meet", or "conflicts with".
- IMPORTANT: Use Plain English throughout. Choose simple, clear words over complex vocabulary. Prefer: "add" over "incorporate/integrate", "about" over "regarding", "qualifies as" over "constitutes", "use" over "utilise", "before" over "prior to", "to" over "in order to", "try" over "endeavour", "then" over "subsequently", "put in place" over "implement", "keep/contain" over "constrain", "standard interest rate" over "reversion rate". Avoid unnecessary jargon (e.g. use "exaggerated claim" instead of "puffery"). Feedback should be easy to understand for all users.

View file

@ -144,9 +144,14 @@ If the proof is nonsensical, not a marketing material, or cannot be analyzed, se
- Then provide your analysis findings below
- Keep feedback brief and scannable
- Use bullet points for each finding
- Each bullet should be one actionable sentence
- Start with the issue, then the specification requirement
- Example: "Image resolution 72dpi - increase to minimum 150dpi for print quality"
- Structure each feedback bullet as two clearly labelled parts separated by a line break:
**Issue:** [Clear description of what's wrong]
**Recommendation:** [Actionable fix what to do and how]
- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**)
- Always place the Recommendation on a new line after the Issue (not on the same line)
- Example:
"• **Issue:** The image resolution is 72 DPI (dots per inch), which is below the minimum for this format.
**Recommendation:** Increase the resolution to at least 150 DPI to meet the platform's quality requirements."
- IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed").
- IMPORTANT: Never use the words "violation", "violates", or "violated" in your output. Use constructive alternatives such as "issue", "doesn't align with", "doesn't meet", or "conflicts with".
- IMPORTANT: Use Plain English throughout. Choose simple, clear words over complex vocabulary. Prefer: "add" over "incorporate/integrate", "about" over "regarding", "qualifies as" over "constitutes", "use" over "utilise", "before" over "prior to", "to" over "in order to", "try" over "endeavour", "then" over "subsequently", "put in place" over "implement", "keep/contain" over "constrain", "standard interest rate" over "reversion rate". Avoid unnecessary jargon (e.g. use "exaggerated claim" instead of "puffery"). Feedback should be easy to understand for all users.

View file

@ -149,8 +149,14 @@ Your summary should:
- For 'Analysis Error': "This proof could not be reliably processed."
- For 'Requires Manual Legal Review': "This proof requires manual legal review."
- Do NOT prefix the opening line with "Verdict:", "Summary:", "Result:", or any other label.
- Follow the opening line with bullet points listing the key actions (max 3-5 bullets)
- Each bullet: one sentence, actionable
- Follow the opening line with bullet points (max 3-5 bullets)
- Structure each bullet as two clearly labelled parts separated by a line break:
**Issue:** [Clear description of what's wrong]
**Recommendation:** [Actionable fix]
- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**)
- Example:
"• **Issue:** The logo is positioned in the top-left corner, which doesn't align with brand guidelines.
**Recommendation:** Move the logo to the bottom-right corner."
- Do NOT include page numbers, document names, or source citations. Keep all feedback self-contained and actionable.
- For 'Passed': briefly note any amber items in 1-2 bullets
- IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed").

View file

@ -145,10 +145,15 @@ If the proof is nonsensical, not a marketing material, or cannot be analyzed, se
**Response Format:**
- Keep feedback brief and scannable
- Use bullet points for each finding
- Each bullet should be one actionable sentence
- Structure each feedback bullet as two clearly labelled parts separated by a line break:
**Issue:** [Clear description of what's wrong]
**Recommendation:** [Actionable fix what to do and how]
- Always capitalise "Issue:" and "Recommendation:" and bold them with double asterisks (**)
- Always place the Recommendation on a new line after the Issue (not on the same line)
- Example:
"• **Issue:** The 'save more' claim is a comparative claim without a defined baseline.
**Recommendation:** Clarify what the customer is saving against (e.g. standard interest rates)."
- Do NOT include page numbers, document names, or source citations (e.g., no "Page 10", "per the Legal Guidelines"). Feedback must be self-contained and actionable.
- Start with the issue, then the requirement
- Example: "Missing APR disclaimer - add representative APR per FCA requirements"
- IMPORTANT: Use British English spelling throughout all output (e.g. "authorised" not "authorized", "colour" not "color", "capitalise" not "capitalize", "organised" not "organized", "centre" not "center", "analysed" not "analyzed").
- IMPORTANT: Never use the words "violation", "violates", or "violated" in your output. Use constructive alternatives such as "issue", "doesn't align with", "doesn't meet", or "conflicts with".
- IMPORTANT: Use Plain English throughout. Choose simple, clear words over complex vocabulary. Prefer: "add" over "incorporate/integrate", "about" over "regarding", "qualifies as" over "constitutes", "use" over "utilise", "before" over "prior to", "to" over "in order to", "try" over "endeavour", "then" over "subsequently", "put in place" over "implement", "keep/contain" over "constrain", "standard interest rate" over "reversion rate". Avoid unnecessary jargon (e.g. use "exaggerated claim" instead of "puffery"). Feedback should be easy to understand for all users.

View file

@ -70,7 +70,7 @@ class GeminiService:
},
"feedback": {
"type": "string",
"description": "Brief bullet-point feedback. Each bullet: one actionable sentence. Format: '• Issue: recommendation'. Max 5 bullets."
"description": "Brief bullet-point feedback. Each bullet has two parts: '**Issue:** [problem]' on one line, then '**Recommendation:** [fix]' on the next line. Use • for bullets. Max 5 bullets."
},
"issues": {
"type": "array",
@ -201,7 +201,7 @@ class GeminiService:
},
"feedback": {
"type": "string",
"description": "Brief bullet-point feedback. Each bullet: one actionable sentence. Format: '• Issue: recommendation'. Max 5 bullets."
"description": "Brief bullet-point feedback. Each bullet has two parts: '**Issue:** [problem]' on one line, then '**Recommendation:** [fix]' on the next line. Use • for bullets. Max 5 bullets."
},
"issues": {
"type": "array",

View file

@ -11,6 +11,31 @@ import { LegalIcon } from './icons/LegalIcon';
* Formats feedback text from Gemini API into properly structured React elements.
* Handles HTML tags, bullet characters, and newline-separated text.
*/
/**
* Renders inline markdown bold (**text**) as <strong> elements.
* Returns an array of React nodes with bold segments wrapped in <strong>.
*/
const renderBoldMarkdown = (text: string): React.ReactNode[] => {
const parts: React.ReactNode[] = [];
const regex = /\*\*(.+?)\*\*/g;
let lastIndex = 0;
let match: RegExpExecArray | null;
while ((match = regex.exec(text)) !== null) {
if (match.index > lastIndex) {
parts.push(text.slice(lastIndex, match.index));
}
parts.push(<strong key={match.index}>{match[1]}</strong>);
lastIndex = regex.lastIndex;
}
if (lastIndex < text.length) {
parts.push(text.slice(lastIndex));
}
return parts;
};
const formatFeedbackText = (text: string): React.ReactNode => {
if (!text) return null;
@ -22,7 +47,7 @@ const formatFeedbackText = (text: string): React.ReactNode => {
// Remove opening tags
.replace(/<ul[^>]*>/gi, '')
.replace(/<li[^>]*>/gi, '• ')
// Remove any other HTML tags
// Remove any other HTML tags (but preserve ** markdown bold)
.replace(/<[^>]+>/g, '')
// Normalize whitespace around bullets
.replace(/\s*•\s*/g, '\n• ')
@ -34,26 +59,22 @@ const formatFeedbackText = (text: string): React.ReactNode => {
const lines = normalizedText.split('\n').filter(line => line.trim());
// Separate intro text from bullet points
const bulletLines: string[] = [];
// Each bullet is an array of lines (to support multi-line Issue/Recommendation)
const bulletGroups: string[][] = [];
const introLines: string[] = [];
lines.forEach(line => {
const trimmed = line.trim();
if (trimmed.startsWith('•')) {
// Remove the bullet character, we'll add it via CSS
bulletLines.push(trimmed.replace(/^•\s*/, '').trim());
// Start a new bullet group
bulletGroups.push([trimmed.replace(/^•\s*/, '').trim()]);
} else if (trimmed) {
// If we haven't started collecting bullets yet, it's intro text
if (bulletLines.length === 0) {
if (bulletGroups.length === 0) {
// Haven't started bullets yet — it's intro text
introLines.push(trimmed);
} else {
// Text after bullets - treat as continuation of last bullet
const lastBullet = bulletLines.pop();
if (lastBullet) {
bulletLines.push(`${lastBullet} ${trimmed}`);
} else {
bulletLines.push(trimmed);
}
// Continuation line within the current bullet
bulletGroups[bulletGroups.length - 1].push(trimmed);
}
}
});
@ -61,12 +82,19 @@ const formatFeedbackText = (text: string): React.ReactNode => {
return (
<>
{introLines.length > 0 && (
<p className="mb-3">{introLines.join(' ')}</p>
<p className="mb-3">{renderBoldMarkdown(introLines.join(' '))}</p>
)}
{bulletLines.length > 0 && (
<ul className="list-disc list-inside space-y-1">
{bulletLines.map((item, index) => (
<li key={index}>{item}</li>
{bulletGroups.length > 0 && (
<ul className="list-disc list-inside space-y-3">
{bulletGroups.map((group, index) => (
<li key={index}>
{group.map((line, lineIdx) => (
<React.Fragment key={lineIdx}>
{lineIdx > 0 && <br />}
{renderBoldMarkdown(line)}
</React.Fragment>
))}
</li>
))}
</ul>
)}

View file

@ -11,6 +11,30 @@ interface PDFReportProps {
proofs: any[];
}
/**
* Renders inline markdown bold (**text**) as <strong> elements for PDF output.
*/
const renderBoldMarkdownForPDF = (text: string): React.ReactNode[] => {
const parts: React.ReactNode[] = [];
const regex = /\*\*(.+?)\*\*/g;
let lastIndex = 0;
let match: RegExpExecArray | null;
while ((match = regex.exec(text)) !== null) {
if (match.index > lastIndex) {
parts.push(text.slice(lastIndex, match.index));
}
parts.push(<strong key={match.index}>{match[1]}</strong>);
lastIndex = regex.lastIndex;
}
if (lastIndex < text.length) {
parts.push(text.slice(lastIndex));
}
return parts;
};
const formatFeedbackTextForPDF = (text: string): React.ReactNode => {
if (!text) return null;
@ -27,23 +51,20 @@ const formatFeedbackTextForPDF = (text: string): React.ReactNode => {
const lines = normalizedText.split('\n').filter(line => line.trim());
const bulletLines: string[] = [];
// Each bullet is an array of lines (to support multi-line Issue/Recommendation)
const bulletGroups: string[][] = [];
const introLines: string[] = [];
lines.forEach(line => {
const trimmed = line.trim();
if (trimmed.startsWith('•')) {
bulletLines.push(trimmed.replace(/^•\s*/, '').trim());
bulletGroups.push([trimmed.replace(/^•\s*/, '').trim()]);
} else if (trimmed) {
if (bulletLines.length === 0) {
if (bulletGroups.length === 0) {
introLines.push(trimmed);
} else {
const lastBullet = bulletLines.pop();
if (lastBullet) {
bulletLines.push(`${lastBullet} ${trimmed}`);
} else {
bulletLines.push(trimmed);
}
// Continuation line within the current bullet
bulletGroups[bulletGroups.length - 1].push(trimmed);
}
}
});
@ -51,12 +72,19 @@ const formatFeedbackTextForPDF = (text: string): React.ReactNode => {
return (
<>
{introLines.length > 0 && (
<p style={{ margin: '0 0 8px 0' }}>{introLines.join(' ')}</p>
<p style={{ margin: '0 0 8px 0' }}>{renderBoldMarkdownForPDF(introLines.join(' '))}</p>
)}
{bulletLines.length > 0 && (
{bulletGroups.length > 0 && (
<ul style={{ margin: 0, paddingLeft: '18px', listStyleType: 'disc' }}>
{bulletLines.map((item, index) => (
<li key={index} style={{ marginBottom: '4px' }}>{item}</li>
{bulletGroups.map((group, index) => (
<li key={index} style={{ marginBottom: '8px' }}>
{group.map((line, lineIdx) => (
<React.Fragment key={lineIdx}>
{lineIdx > 0 && <br />}
{renderBoldMarkdownForPDF(line)}
</React.Fragment>
))}
</li>
))}
</ul>
)}