Skip to content

Commit 2838716

Browse files
feat(flaky-unit-test-analysis): enhance snippet handling and output formatting
- Updated `finalize-schema.json` to include a detailed description for the `snippet` field, ensuring it captures the exact code being replaced. - Modified `task-prompt.md` to clarify that the `snippet` must be verbatim and formatted correctly for rendering diffs. - Refactored `flaky-sticky-comment.ts` to improve the rendering of suggested fixes, ensuring proper indentation and formatting for better readability in comments. - Enhanced the findings section to include links to the analyzed file locations, improving context for reviewers.
1 parent 8ccde0c commit 2838716

5 files changed

Lines changed: 53 additions & 16 deletions

File tree

.ai-pr-analyzer/modes/flaky-unit-test-analysis/finalize-schema.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
"type": "string",
3434
"enum": ["critical", "high", "medium"]
3535
},
36-
"snippet": { "type": "string" },
36+
"snippet": {
37+
"type": "string",
38+
"description": "The exact current code being replaced, covering the same lines/scope as `suggestedFix`, formatted as real TypeScript with line breaks (use \\n between lines and preserve indentation). Must line up with `suggestedFix` so the two can be rendered as a diff (removed vs added lines) — do not paraphrase or summarize it."
39+
},
3740
"explanation": { "type": "string" },
3841
"suggestedFix": {
3942
"type": "string",
@@ -46,6 +49,7 @@
4649
"patternId",
4750
"patternName",
4851
"severity",
52+
"snippet",
4953
"explanation",
5054
"suggestedFix",
5155
"historicalHintUsed"

.ai-pr-analyzer/modes/flaky-unit-test-analysis/task-prompt.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ For each file:
1818
3. Check .ai-pr-analyzer/flaky-history.json (read_file) for a historical hint on this file.
1919
4. Match against the J1-J10 patterns from the loaded skill.
2020
5. For every match, record: file, line, patternId, patternName, severity, snippet, explanation, suggestedFix, and whether the historical hint was used.
21+
- `snippet` MUST be the exact current code being replaced, copied verbatim from the file (no paraphrasing or summarizing), covering the same lines/scope as `suggestedFix` so the two can be rendered as a before/after diff.
2122
- `suggestedFix` MUST be the corrected code snippet ONLY, formatted as real TypeScript with actual line breaks (`\n`) and indentation — never a single-line prose paragraph. Keep all reasoning and instructions in `explanation`.
2223

2324
If a file has no matches, do not invent findings — omit it from findings.

.github/scripts/flaky-sticky-comment.ts

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,43 @@ function buildHistoryTable(
166166
return `Failures / runs sampled per window:\n\n${header}\n${divider}\n${rows}\n`;
167167
}
168168

169-
function buildFindingsSection(findings: Finding[]): string {
169+
// Every line of a fenced code block nested inside a list item must carry the
170+
// same 4-space indent, or Markdown treats it as outside the list and the
171+
// fence collapses.
172+
function indentBlock(text: string): string {
173+
return text
174+
.split('\n')
175+
.map((line) => ` ${line}`)
176+
.join('\n');
177+
}
178+
179+
// Renders the fix as a ```diff fence (snippet lines as `-`, suggestedFix
180+
// lines as `+`) so reviewers see a before/after instead of only the new
181+
// code. This is a whole-block replace rather than a computed line diff:
182+
// the AI's `snippet` isn't guaranteed to line up 1:1 with `suggestedFix`,
183+
// and a mismatched line-level diff would look more broken than helpful.
184+
// Falls back to a plain `ts` block when no snippet was captured.
185+
function buildFixBlock(f: Finding): string {
186+
if (!f.snippet) {
187+
return ` \`\`\`ts\n${indentBlock(f.suggestedFix)}\n \`\`\``;
188+
}
189+
const removed = f.snippet.split('\n').map((line) => `-${line}`);
190+
const added = f.suggestedFix.split('\n').map((line) => `+${line}`);
191+
const diff = [...removed, ...added].join('\n');
192+
return ` \`\`\`diff\n${indentBlock(diff)}\n \`\`\``;
193+
}
194+
195+
// file#L<line> anchor pointing at the analyzed SHA so the link stays valid
196+
// even after later pushes move the head.
197+
function buildLocationLink(file: string, line: number | undefined, headSha: string): string {
198+
const label = line ? `${file}:${line}` : file;
199+
if (!headSha) return `\`${label}\``;
200+
const anchor = line ? `#L${line}` : '';
201+
const url = `${env.serverUrl}/${env.repo}/blob/${headSha}/${file}${anchor}`;
202+
return `[\`${label}\`](${url})`;
203+
}
204+
205+
function buildFindingsSection(findings: Finding[], headSha: string): string {
170206
if (findings.length === 0) return '';
171207
const byFile = new Map<string, Finding[]>();
172208
for (const finding of findings) {
@@ -179,16 +215,10 @@ function buildFindingsSection(findings: Finding[]): string {
179215
out += `#### \`${file}\`\n\n`;
180216
for (const f of fileFindings) {
181217
const hint = f.historicalHintUsed ? ' _(matches historical failure signal)_' : '';
182-
out += `- **${f.patternId}${f.patternName}** (${f.severity})${hint}${f.line ? ` — line ${f.line}` : ''}\n`;
218+
out += `- **${f.patternId}${f.patternName}** (${f.severity})${hint}\n`;
183219
out += ` - ${f.explanation}\n`;
184-
// Every line of the fix must carry the 4-space indent that keeps the
185-
// fenced block nested inside this list item; indenting only the first
186-
// line would push the rest out of the list and collapse the code block.
187-
const indentedFix = f.suggestedFix
188-
.split('\n')
189-
.map((line) => ` ${line}`)
190-
.join('\n');
191-
out += ` - Suggested fix:\n \`\`\`ts\n${indentedFix}\n \`\`\`\n`;
220+
const location = buildLocationLink(f.file, f.line, headSha);
221+
out += ` - Suggested fix in ${location}:\n${buildFixBlock(f)}\n`;
192222
}
193223
out += '\n';
194224
}
@@ -202,13 +232,15 @@ function buildCommentBody({
202232
windows,
203233
runsSampled,
204234
stateBlock,
235+
headSha,
205236
}: {
206237
historyFiles: HistoryFile[];
207238
findings: Finding[];
208239
runHistoryUrl: string;
209240
windows: number[];
210241
runsSampled: WindowCounts;
211242
stateBlock: string;
243+
headSha: string;
212244
}): string {
213245
// A file surfaces in the run-history table if it's historically flaky OR
214246
// an AI finding calls it out — otherwise an AI-only finding (zero
@@ -217,7 +249,7 @@ function buildCommentBody({
217249
const findingFiles = new Set(findings.map((f) => f.file));
218250
const tableFiles = historyFiles.filter((f) => f.flaky || findingFiles.has(f.path));
219251
const historyTable = buildHistoryTable(tableFiles, windows, runsSampled);
220-
const findingsSection = buildFindingsSection(findings);
252+
const findingsSection = buildFindingsSection(findings, headSha);
221253

222254
return `${MARKER}
223255
## 🧪 Flaky unit test detection
@@ -356,7 +388,7 @@ async function main(): Promise<void> {
356388
owner,
357389
repo,
358390
issue_number: env.prNumber,
359-
body: buildCommentBody({ historyFiles, findings: mergedFindings, runHistoryUrl, windows, runsSampled, stateBlock }),
391+
body: buildCommentBody({ historyFiles, findings: mergedFindings, runHistoryUrl, windows, runsSampled, stateBlock, headSha }),
360392
});
361393
console.log('📝 Created sticky flaky-test-detection comment');
362394
return;
@@ -367,7 +399,7 @@ async function main(): Promise<void> {
367399
owner,
368400
repo,
369401
comment_id: existingComment.id,
370-
body: buildCommentBody({ historyFiles, findings: mergedFindings, runHistoryUrl, windows, runsSampled, stateBlock }),
402+
body: buildCommentBody({ historyFiles, findings: mergedFindings, runHistoryUrl, windows, runsSampled, stateBlock, headSha }),
371403
});
372404
console.log('🔄 Updated sticky flaky-test-detection comment with latest findings');
373405
return;

app/components/Base/RemoteImage/index.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ describe('RemoteImage', () => {
9393
mockGetFormattedIpfsUrl.mockImplementation(createPendingIpfsResolution);
9494
});
9595

96-
// [mcwp-474-tmp 2/5]
96+
// [mcwp-474-tmp 2/52]
9797
it('renders svg correctly', () => {
9898
const { UNSAFE_getByType } = render(
9999
<RemoteImage

app/components/UI/SimulationDetails/useBalanceChanges.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ describe('useBalanceChanges', () => {
203203
);
204204
};
205205

206-
// [mcwp-474-tmp 3/5]
206+
// [mcwp-474-tmp 3/52]
207207
it('maps token balance changes correctly', async () => {
208208
const { result } = setupHook([
209209
{

0 commit comments

Comments
 (0)