Summary
Heap-efficiency improvement in WorkflowHistoryRead.getHistory(List<Id>) (bulk read). Surfaced by Codex on PR #216. Severity P2 — deferred from that PR per the campaign's "only verified P1s hold the gate" rule. Behavior is correct today (right results, no throw for realistic input); this is an efficiency/robustness hardening, not a bug.
Context
The bulk scan in queryRowsByInstance (~WorkflowHistoryRead.cls:318) accumulates every scanned row into a per-instance bucket, ordered Workflow_Instance__c ASC, CreatedDate ASC, Id ASC, under a single shared scanLimit. The StepHistory projection later keeps only the earliest MAX_HISTORY_ROWS (500) rows per instance, and the authoritative totalCount comes from the grouped COUNT() in realStepTotals — not from the scanned rows.
Note the scan SELECT no longer includes any LongTextArea/blob fields — Error_Details__c / Input__c / Output__c / Captured_Values__c were removed, so scanned rows are lightweight scalar SObjects. This significantly reduces the heap pressure versus the earlier payload-carrying scan.
Before / After
Before: A bulk getHistory(List<Id>) call where one requested instance has far more than MAX_HISTORY_ROWS (500) executions and sorts first can accumulate up to the whole scan budget of SObjects in one heap bucket before the StepHistory projection discards all but 500. Under enough volume (a single hot instance with tens of thousands of executions in one bulk call), this can approach Apex heap limits even though rows are now blob-free.
After: During the bulk scan accumulation loop (~WorkflowHistoryRead.cls:318), stop adding rows to a per-instance bucket once it reaches MAX_HISTORY_ROWS. The authoritative totalCount already comes from the grouped COUNT(), so trimming does not affect totalCount or isTruncated — a scan-starved / over-cap instance is still reported with its true total and isTruncated = true. Only the redundant rows that projection would discard anyway are dropped, capping the retained heap per instance at the cap.
Proposed fix
In the accumulation loop, skip bucket.add(row) once bucket.size() >= MAX_HISTORY_ROWS. No change to totalCount (grouped COUNT is authoritative) or to the truncation flag.
Provenance
Summary
Heap-efficiency improvement in
WorkflowHistoryRead.getHistory(List<Id>)(bulk read). Surfaced by Codex on PR #216. Severity P2 — deferred from that PR per the campaign's "only verified P1s hold the gate" rule. Behavior is correct today (right results, no throw for realistic input); this is an efficiency/robustness hardening, not a bug.Context
The bulk scan in
queryRowsByInstance(~WorkflowHistoryRead.cls:318) accumulates every scanned row into a per-instance bucket, orderedWorkflow_Instance__c ASC, CreatedDate ASC, Id ASC, under a single sharedscanLimit. TheStepHistoryprojection later keeps only the earliestMAX_HISTORY_ROWS(500) rows per instance, and the authoritativetotalCountcomes from the groupedCOUNT()inrealStepTotals— not from the scanned rows.Note the scan
SELECTno longer includes any LongTextArea/blob fields —Error_Details__c/Input__c/Output__c/Captured_Values__cwere removed, so scanned rows are lightweight scalar SObjects. This significantly reduces the heap pressure versus the earlier payload-carrying scan.Before / After
Before: A bulk
getHistory(List<Id>)call where one requested instance has far more thanMAX_HISTORY_ROWS(500) executions and sorts first can accumulate up to the whole scan budget of SObjects in one heap bucket before theStepHistoryprojection discards all but 500. Under enough volume (a single hot instance with tens of thousands of executions in one bulk call), this can approach Apex heap limits even though rows are now blob-free.After: During the bulk scan accumulation loop (~
WorkflowHistoryRead.cls:318), stop adding rows to a per-instance bucket once it reachesMAX_HISTORY_ROWS. The authoritativetotalCountalready comes from the groupedCOUNT(), so trimming does not affecttotalCountorisTruncated— a scan-starved / over-cap instance is still reported with its true total andisTruncated = true. Only the redundant rows that projection would discard anyway are dropped, capping the retained heap per instance at the cap.Proposed fix
In the accumulation loop, skip
bucket.add(row)oncebucket.size() >= MAX_HISTORY_ROWS. No change tototalCount(grouped COUNT is authoritative) or to the truncation flag.Provenance
e536b509d6): getHistory: ordered step-history DTOs (#85) #216