Skip to content

Commit a94b8fb

Browse files
committed
Jetpack AI: survive an editorial review implication with no affected_blocks
The structured-output schema lists affected_blocks as required, but the tool is not strict, so the model can leave it out. Reading .length off it threw "Cannot read properties of undefined" and unmounted the whole review card over one missing field. The field is now optional on the type and guarded at the render site. Paired with a server-side backfill in wpcom, which repairs already-cached payloads; this guard covers any that reach an older client.
1 parent 5c295ee commit a94b8fb

2 files changed

Lines changed: 45 additions & 22 deletions

File tree

packages/jetpack-ai-sidebar/src/components/ai-editorial-review.test.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,23 @@ describe( 'AiEditorialReview — smoke render', () => {
465465
expect( mockedRecordTracksEvent ).not.toHaveBeenCalled();
466466
} );
467467

468+
it( 'renders an implication that omits affected_blocks', () => {
469+
// The tool schema marks affected_blocks required but the tool is not strict,
470+
// so the model can omit it. Indexing it directly used to blank the card.
471+
render(
472+
<AiEditorialReview
473+
{ ...basePayload( {
474+
implications: [
475+
{ change: 'Tone shift', implies: 'May affect downstream FAQ wording.' },
476+
] as any,
477+
} ) }
478+
/>
479+
);
480+
481+
expect( screen.getByText( 'Tone shift' ) ).toBeInTheDocument();
482+
expect( screen.queryByText( 'Affects:' ) ).not.toBeInTheDocument();
483+
} );
484+
468485
it( 'does not tag a stale AER edit "Manual edit" even when the source text is absent', () => {
469486
// Editor moved to post 999 (stale) AND the current block doesn't contain the
470487
// edit's source text — so the frontend reason is truthy. Without the

packages/jetpack-ai-sidebar/src/components/ai-editorial-review.tsx

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ interface Conflict {
7373
interface Implication {
7474
change: string;
7575
implies: string;
76-
affected_blocks: number[];
76+
affected_blocks?: number[];
7777
}
7878

7979
interface SuggestedEdit {
@@ -1389,27 +1389,33 @@ export default function AiEditorialReview( {
13891389
onToggle={ ( next: boolean ) => setSectionOpen( 'implications', next ) }
13901390
>
13911391
<ul>
1392-
{ implications.map( ( imp, i ) => (
1393-
<li key={ `imp-${ i }` }>
1394-
<strong>{ imp.change }</strong>{ imp.implies }
1395-
{ imp.affected_blocks.length > 0 && (
1396-
<span className="jetpack-ai-editorial-review__affected-blocks">
1397-
{ ' ' }
1398-
{ __( 'Affects:', __i18n_text_domain__ ) }{ ' ' }
1399-
{ imp.affected_blocks.map( ( b, j ) => (
1400-
<span key={ `imp-${ i }-aff-${ j }` }>
1401-
{ j > 0 && ', ' }
1402-
<BlockRef
1403-
index={ b }
1404-
blocks={ blocks }
1405-
onFocus={ focusCurrentPostBlock }
1406-
/>
1407-
</span>
1408-
) ) }
1409-
</span>
1410-
) }
1411-
</li>
1412-
) ) }
1392+
{ implications.map( ( imp, i ) => {
1393+
// A non-strict payload can omit affected_blocks.
1394+
const affectedBlocks = Array.isArray( imp.affected_blocks )
1395+
? imp.affected_blocks
1396+
: [];
1397+
return (
1398+
<li key={ `imp-${ i }` }>
1399+
<strong>{ imp.change }</strong>{ imp.implies }
1400+
{ affectedBlocks.length > 0 && (
1401+
<span className="jetpack-ai-editorial-review__affected-blocks">
1402+
{ ' ' }
1403+
{ __( 'Affects:', __i18n_text_domain__ ) }{ ' ' }
1404+
{ affectedBlocks.map( ( b, j ) => (
1405+
<span key={ `imp-${ i }-aff-${ j }` }>
1406+
{ j > 0 && ', ' }
1407+
<BlockRef
1408+
index={ b }
1409+
blocks={ blocks }
1410+
onFocus={ focusCurrentPostBlock }
1411+
/>
1412+
</span>
1413+
) ) }
1414+
</span>
1415+
) }
1416+
</li>
1417+
);
1418+
} ) }
14131419
</ul>
14141420
</PanelBody>
14151421
</div>

0 commit comments

Comments
 (0)