@@ -295,19 +295,16 @@ function createFileDragEvent(type: string, files: File[]) {
295295
296296describe ( "IssueChatThread" , ( ) => {
297297 let container : HTMLDivElement ;
298- const originalDocumentElementScrollIntoView = document . documentElement . scrollIntoView ;
299298
300299 beforeEach ( ( ) => {
301300 container = document . createElement ( "div" ) ;
302301 document . body . appendChild ( container ) ;
303302 window . scrollTo = vi . fn ( ) ;
304- document . documentElement . scrollIntoView = vi . fn ( ) as unknown as typeof document . documentElement . scrollIntoView ;
305303 localStorage . clear ( ) ;
306304 } ) ;
307305
308306 afterEach ( ( ) => {
309307 container . remove ( ) ;
310- document . documentElement . scrollIntoView = originalDocumentElementScrollIntoView ;
311308 vi . useRealTimers ( ) ;
312309 appendMock . mockReset ( ) ;
313310 markdownEditorFocusMock . mockReset ( ) ;
@@ -330,7 +327,6 @@ describe("IssueChatThread", () => {
330327 liveRuns = { [ ] }
331328 onAdd = { async ( ) => { } }
332329 showComposer = { false }
333- newestFirst
334330 enableLiveTranscriptPolling = { false }
335331 />
336332 </ MemoryRouter > ,
@@ -340,16 +336,6 @@ describe("IssueChatThread", () => {
340336 expect ( container . textContent ) . toContain ( "Jump to latest" ) ;
341337 expect ( container . textContent ) . not . toContain ( "Chat (" ) ;
342338
343- const threadRoot = container . querySelector ( '[data-testid="thread-root"]' ) ;
344- const jumpButton = Array . from ( container . querySelectorAll ( "button" ) ) . find (
345- ( button ) => button . textContent === "Jump to latest" ,
346- ) ;
347- expect ( threadRoot ) . not . toBeNull ( ) ;
348- expect ( jumpButton ) . toBeDefined ( ) ;
349- expect (
350- threadRoot ?. compareDocumentPosition ( jumpButton ! ) ,
351- ) . toBe ( Node . DOCUMENT_POSITION_FOLLOWING ) ;
352-
353339 const viewport = container . querySelector ( '[data-testid="thread-viewport"]' ) as HTMLDivElement | null ;
354340 expect ( viewport ) . not . toBeNull ( ) ;
355341 expect ( viewport ?. className ) . not . toContain ( "overflow-y-auto" ) ;
@@ -360,106 +346,6 @@ describe("IssueChatThread", () => {
360346 } ) ;
361347 } ) ;
362348
363- it ( "defaults to oldest-first rendering and jump placement" , ( ) => {
364- const root = createRoot ( container ) ;
365-
366- act ( ( ) => {
367- root . render (
368- < MemoryRouter >
369- < IssueChatThread
370- comments = { [
371- {
372- id : "comment-older" ,
373- companyId : "company-1" ,
374- issueId : "issue-1" ,
375- authorAgentId : "agent-1" ,
376- authorUserId : null ,
377- body : "Older comment" ,
378- authorType : "agent" ,
379- presentation : null ,
380- metadata : null ,
381- createdAt : new Date ( "2026-04-06T12:00:00.000Z" ) ,
382- updatedAt : new Date ( "2026-04-06T12:00:00.000Z" ) ,
383- } ,
384- {
385- id : "comment-newer" ,
386- companyId : "company-1" ,
387- issueId : "issue-1" ,
388- authorAgentId : "agent-1" ,
389- authorUserId : null ,
390- body : "Newer comment" ,
391- authorType : "agent" ,
392- presentation : null ,
393- metadata : null ,
394- createdAt : new Date ( "2026-04-06T12:01:00.000Z" ) ,
395- updatedAt : new Date ( "2026-04-06T12:01:00.000Z" ) ,
396- } ,
397- ] }
398- linkedRuns = { [ ] }
399- timelineEvents = { [ ] }
400- liveRuns = { [ ] }
401- onAdd = { async ( ) => { } }
402- showComposer = { false }
403- enableLiveTranscriptPolling = { false }
404- />
405- </ MemoryRouter > ,
406- ) ;
407- } ) ;
408-
409- const rows = Array . from ( container . querySelectorAll ( '[data-testid="issue-chat-message-row"]' ) ) ;
410- expect ( rows [ 0 ] ?. textContent ) . toContain ( "Older comment" ) ;
411- expect ( rows [ 1 ] ?. textContent ) . toContain ( "Newer comment" ) ;
412-
413- const threadRoot = container . querySelector ( '[data-testid="thread-root"]' ) ;
414- const jumpButton = Array . from ( container . querySelectorAll ( "button" ) ) . find (
415- ( button ) => button . textContent === "Jump to latest" ,
416- ) ;
417- expect ( threadRoot ) . not . toBeNull ( ) ;
418- expect ( jumpButton ) . toBeDefined ( ) ;
419- expect (
420- threadRoot ?. compareDocumentPosition ( jumpButton ! ) ,
421- ) . toBe ( Node . DOCUMENT_POSITION_PRECEDING ) ;
422-
423- act ( ( ) => {
424- root . unmount ( ) ;
425- } ) ;
426- } ) ;
427-
428- it ( "renders the jump control above the thread when newest-first mode is disabled" , ( ) => {
429- const root = createRoot ( container ) ;
430-
431- act ( ( ) => {
432- root . render (
433- < MemoryRouter >
434- < IssueChatThread
435- comments = { [ ] }
436- linkedRuns = { [ ] }
437- timelineEvents = { [ ] }
438- liveRuns = { [ ] }
439- onAdd = { async ( ) => { } }
440- showComposer = { false }
441- newestFirst = { false }
442- enableLiveTranscriptPolling = { false }
443- />
444- </ MemoryRouter > ,
445- ) ;
446- } ) ;
447-
448- const threadRoot = container . querySelector ( '[data-testid="thread-root"]' ) ;
449- const jumpButton = Array . from ( container . querySelectorAll ( "button" ) ) . find (
450- ( button ) => button . textContent === "Jump to latest" ,
451- ) ;
452- expect ( threadRoot ) . not . toBeNull ( ) ;
453- expect ( jumpButton ) . toBeDefined ( ) ;
454- expect (
455- threadRoot ?. compareDocumentPosition ( jumpButton ! ) ,
456- ) . toBe ( Node . DOCUMENT_POSITION_PRECEDING ) ;
457-
458- act ( ( ) => {
459- root . unmount ( ) ;
460- } ) ;
461- } ) ;
462-
463349 it ( "renders the composer in planning mode when the issue is in planning mode" , ( ) => {
464350 const root = createRoot ( container ) ;
465351
@@ -1073,7 +959,6 @@ describe("IssueChatThread", () => {
1073959 agentMap = { issueChatLongThreadAgentMap }
1074960 currentUserId = "user-board"
1075961 onAdd = { async ( ) => { } }
1076- newestFirst
1077962 enableLiveTranscriptPolling = { false }
1078963 onRefreshLatestComments = { async ( ) => {
1079964 setComments ( [ olderComment , latestComment ] ) ;
@@ -1110,15 +995,15 @@ describe("IssueChatThread", () => {
1110995 } ) ;
1111996 } ) ;
1112997
1113- it ( "findLatestCommentMessageIndex prefers the first comment-anchored row when newest renders first " , ( ) => {
998+ it ( "findLatestCommentMessageIndex prefers the last comment-anchored row (PAP-2672) " , ( ) => {
1114999 const messages = [
11151000 { metadata : { custom : { anchorId : "comment-a" } } } ,
11161001 { metadata : { custom : { anchorId : "run-1" } } } ,
11171002 { metadata : { custom : { anchorId : "comment-b" } } } ,
11181003 { metadata : { custom : { anchorId : "run-2" } } } ,
11191004 { metadata : { custom : { anchorId : "activity-3" } } } ,
11201005 ] ;
1121- expect ( findLatestCommentMessageIndex ( messages as never ) ) . toBe ( 0 ) ;
1006+ expect ( findLatestCommentMessageIndex ( messages as never ) ) . toBe ( 2 ) ;
11221007 expect (
11231008 findLatestCommentMessageIndex ( [
11241009 { metadata : { custom : { anchorId : "run-only" } } } ,
@@ -1127,17 +1012,6 @@ describe("IssueChatThread", () => {
11271012 expect ( findLatestCommentMessageIndex ( [ ] as never ) ) . toBe ( - 1 ) ;
11281013 } ) ;
11291014
1130- it ( "findLatestCommentMessageIndex prefers the last comment-anchored row when newest-first mode is disabled" , ( ) => {
1131- const messages = [
1132- { metadata : { custom : { anchorId : "comment-a" } } } ,
1133- { metadata : { custom : { anchorId : "run-1" } } } ,
1134- { metadata : { custom : { anchorId : "comment-b" } } } ,
1135- { metadata : { custom : { anchorId : "run-2" } } } ,
1136- { metadata : { custom : { anchorId : "activity-3" } } } ,
1137- ] ;
1138- expect ( findLatestCommentMessageIndex ( messages as never , false ) ) . toBe ( 2 ) ;
1139- } ) ;
1140-
11411015 it ( "keeps the direct render path for short threads under the virtualization threshold" , ( ) => {
11421016 const root = createRoot ( container ) ;
11431017 const directComments = issueChatLongThreadComments . slice ( 0 , 12 ) ;
@@ -1846,50 +1720,6 @@ describe("IssueChatThread", () => {
18461720 } ) ;
18471721 } ) ;
18481722
1849- it ( "renders the comment timestamp above the comment body" , ( ) => {
1850- vi . useFakeTimers ( ) ;
1851- vi . setSystemTime ( new Date ( "2026-04-08T12:00:00.000Z" ) ) ;
1852- const root = createRoot ( container ) ;
1853-
1854- act ( ( ) => {
1855- root . render (
1856- < MemoryRouter >
1857- < IssueChatThread
1858- comments = { [ {
1859- id : "comment-1" ,
1860- companyId : "company-1" ,
1861- issueId : "issue-1" ,
1862- authorAgentId : "agent-1" ,
1863- authorUserId : null ,
1864- body : "Agent summary" ,
1865- authorType : "agent" ,
1866- presentation : null ,
1867- metadata : null ,
1868- createdAt : new Date ( "2026-04-06T12:00:00.000Z" ) ,
1869- updatedAt : new Date ( "2026-04-06T12:00:00.000Z" ) ,
1870- } ] }
1871- linkedRuns = { [ ] }
1872- timelineEvents = { [ ] }
1873- liveRuns = { [ ] }
1874- onAdd = { async ( ) => { } }
1875- showComposer = { false }
1876- newestFirst
1877- enableLiveTranscriptPolling = { false }
1878- />
1879- </ MemoryRouter > ,
1880- ) ;
1881- } ) ;
1882-
1883- const text = container . textContent ?? "" ;
1884- const timestampIndex = text . indexOf ( "2d ago" ) ;
1885- expect ( timestampIndex ) . toBeGreaterThanOrEqual ( 0 ) ;
1886- expect ( timestampIndex ) . toBeLessThan ( text . indexOf ( "Agent summary" ) ) ;
1887-
1888- act ( ( ) => {
1889- root . unmount ( ) ;
1890- } ) ;
1891- } ) ;
1892-
18931723 it ( "shows deferred wake badge only for hold-deferred queued comments" , ( ) => {
18941724 const root = createRoot ( container ) ;
18951725
0 commit comments