Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions apps/frontend/src/renderer/components/context/MemoryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ interface ParsedSessionInsight {
spec_id?: string;
session_number?: number;
subtasks_completed?: string[];
what_worked?: string[];
what_failed?: string[];
recommendations_for_next_session?: string[];
what_worked?: Array<string | Record<string, unknown>>;
what_failed?: Array<string | Record<string, unknown>>;
recommendations_for_next_session?: Array<string | Record<string, unknown>>;
discoveries?: {
file_insights?: Array<{ path?: string; purpose?: string; changes_made?: string }>;
patterns_discovered?: Array<{ pattern?: string; applies_to?: string } | string>;
Expand All @@ -39,11 +39,35 @@ interface ParsedSessionInsight {
why_it_worked?: string;
why_it_failed?: string;
};
recommendations?: string[];
recommendations?: Array<string | Record<string, unknown>>;
changed_files?: string[];
};
}

function toSafeString(item: unknown): string {
if (typeof item === 'string') return item;
if (item === null || item === undefined) return '';
if (Array.isArray(item)) return item.map(i => toSafeString(i)).join(', ');
if (typeof item === 'object') {
const obj = item as Record<string, unknown>;
// Handle {category, recommendation} object - the known cause of React Error #31
if (typeof obj.category === 'string' && typeof obj.recommendation === 'string') {
return `[${obj.category}] ${obj.recommendation}`;
}
// Try other common single-field patterns
if (typeof obj.recommendation === 'string') return obj.recommendation;
if (typeof obj.text === 'string') return obj.text;
if (typeof obj.description === 'string') return obj.description;
if (typeof obj.message === 'string') return obj.message;
// Fallback: combine all string values from the object
const parts = Object.entries(obj)
.filter(([, v]) => typeof v === 'string')
.map(([k, v]) => `${k}: ${v}`);
return parts.length > 0 ? parts.join(', ') : JSON.stringify(item);
}
return String(item);
}
Comment on lines +47 to +69
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

toSafeString correctly addresses the root cause; two minor follow-ups.

Both issues raised in the previous review cycle (unknown type signature and Array.isArray guard) are properly addressed. Two remaining points:

  1. Line 50 — nitpick: item.map(i => toSafeString(i)) is a wrapper that can be replaced with the point-free form.
♻️ Proposed simplification
-  if (Array.isArray(item)) return item.map(i => toSafeString(i)).join(', ');
+  if (Array.isArray(item)) return item.map(toSafeString).join(', ');
  1. Line 66 — UX fallback: For objects where all field values are non-string (e.g., { count: 5, nested: {} }), parts is empty and JSON.stringify(item) is returned directly into the UI. This is safe for React, but users may see raw JSON. Consider a friendlier sentinel like '[complex value]' if raw JSON would be confusing in this context.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function toSafeString(item: unknown): string {
if (typeof item === 'string') return item;
if (item === null || item === undefined) return '';
if (Array.isArray(item)) return item.map(i => toSafeString(i)).join(', ');
if (typeof item === 'object') {
const obj = item as Record<string, unknown>;
// Handle {category, recommendation} object - the known cause of React Error #31
if (typeof obj.category === 'string' && typeof obj.recommendation === 'string') {
return `[${obj.category}] ${obj.recommendation}`;
}
// Try other common single-field patterns
if (typeof obj.recommendation === 'string') return obj.recommendation;
if (typeof obj.text === 'string') return obj.text;
if (typeof obj.description === 'string') return obj.description;
if (typeof obj.message === 'string') return obj.message;
// Fallback: combine all string values from the object
const parts = Object.entries(obj)
.filter(([, v]) => typeof v === 'string')
.map(([k, v]) => `${k}: ${v}`);
return parts.length > 0 ? parts.join(', ') : JSON.stringify(item);
}
return String(item);
}
function toSafeString(item: unknown): string {
if (typeof item === 'string') return item;
if (item === null || item === undefined) return '';
if (Array.isArray(item)) return item.map(toSafeString).join(', ');
if (typeof item === 'object') {
const obj = item as Record<string, unknown>;
// Handle {category, recommendation} object - the known cause of React Error `#31`
if (typeof obj.category === 'string' && typeof obj.recommendation === 'string') {
return `[${obj.category}] ${obj.recommendation}`;
}
// Try other common single-field patterns
if (typeof obj.recommendation === 'string') return obj.recommendation;
if (typeof obj.text === 'string') return obj.text;
if (typeof obj.description === 'string') return obj.description;
if (typeof obj.message === 'string') return obj.message;
// Fallback: combine all string values from the object
const parts = Object.entries(obj)
.filter(([, v]) => typeof v === 'string')
.map(([k, v]) => `${k}: ${v}`);
return parts.length > 0 ? parts.join(', ') : JSON.stringify(item);
}
return String(item);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/frontend/src/renderer/components/context/MemoryCard.tsx` around lines 47
- 69, toSafeString currently maps arrays with an explicit lambda and returns
JSON.stringify(item) for objects with no string fields; change the array mapping
to the point-free form by using item.map(toSafeString) in the Array.isArray
branch, and replace the UX fallback in the object branch (where parts.length ===
0) to return a friendlier sentinel like "[complex value]" instead of raw
JSON.stringify(item) so users don't see raw JSON; the function to update is
toSafeString and adjust the Array.isArray branch and the final object fallback
accordingly.


function parseMemoryContent(content: string): ParsedSessionInsight | null {
try {
return JSON.parse(content);
Expand Down Expand Up @@ -207,7 +231,7 @@ export function MemoryCard({ memory }: MemoryCardProps) {
<SectionHeader icon={CheckCircle2} title="What Worked" count={parsed.what_worked.length} />
<ul className="space-y-0.5">
{parsed.what_worked.map((item, idx) => (
<ListItem key={idx} variant="success">{item}</ListItem>
<ListItem key={idx} variant="success">{toSafeString(item)}</ListItem>
))}
</ul>
</div>
Expand All @@ -219,7 +243,7 @@ export function MemoryCard({ memory }: MemoryCardProps) {
<SectionHeader icon={XCircle} title="What Failed" count={parsed.what_failed.length} />
<ul className="space-y-0.5">
{parsed.what_failed.map((item, idx) => (
<ListItem key={idx} variant="error">{item}</ListItem>
<ListItem key={idx} variant="error">{toSafeString(item)}</ListItem>
))}
</ul>
</div>
Expand Down Expand Up @@ -261,10 +285,10 @@ export function MemoryCard({ memory }: MemoryCardProps) {
/>
<ul className="space-y-0.5">
{parsed.recommendations_for_next_session?.map((item, idx) => (
<ListItem key={`rec-${idx}`}>{item}</ListItem>
<ListItem key={`rec-${idx}`}>{toSafeString(item)}</ListItem>
))}
{parsed.discoveries?.recommendations?.map((item, idx) => (
<ListItem key={`disc-rec-${idx}`}>{item}</ListItem>
<ListItem key={`disc-rec-${idx}`}>{toSafeString(item)}</ListItem>
))}
</ul>
</div>
Expand Down
Loading