Bug 12656 allows one to define a cancellation reason when running the cancel_expired_holds cron. That reason is recorded for later reporting and also triggers a notice. That cron cancels both holds that have simply never had an item captured *and* holds where the item has been waiting on the hold shelf past the pickup delay. Ideally, we'd be able to tell the cron to apply two different reasons in these two very distinct cases. Maybe the cron could take a pair of [found value]=[reason]? So you could set it with something like: 'W'='TOO_LONG' NULL='UNFILLED'
Actually, reiterating the importance of this a bit, if a library is using ExpireReservesMaxPickUpDelayCharge then an auto-cancelled waiting hold will result in a fee but an auto-cancelled non-waiting hold will not. That's not new behavior or anything, but it definitely would be helpful to be able to easily know that in the notice.