the answer is that it was all rows returned. The entire select took the 15 seconds and returned 5599 rows:

insert /*+ APPEND */ into LDRWORKED
select /*+ INDEX(s SHIFTSFAC h DUTY_TBLTASK_HISTORY_PK) */ s.SHIFTNUMBER, h.START_TIME_LCL, h.END_TIME_LCL, 'AT', h.PROJECT_NUM, h.TASK_NUM, h.DATETIMESTAMP
from SHIFTS s,
msdt.duty_tblTASK_HISTORY h
where s.FACILITYID = 'EA-N90-TRACON'
and h.FAC_IDENT = 'EA-N90-TRACON'
and s.EMPLOYEEID = h.EMPLOYEE_ID
and h.START_TIME_LCL between s.SHIFTSTART and s.SHIFTSTOP
and h.PROJECT_NUM is not null
and h.TASK_NUM is not null

Operation Object Name Rows Bytes Cost Object Node In/Out PStart PStop

SELECT STATEMENT Optimizer Mode=ALL_ROWS 7 K 5387
HASH JOIN 7 K 729 K 5387
TABLE ACCESS BY INDEX ROWID SWARM.SHIFTS 30 K 1 M 517
INDEX RANGE SCAN SWARM.SHIFTSFAC 30 K 164
TABLE ACCESS FULL MSDT.DUTY_TBLTASK_HISTORY 45 K 2 M 4643