diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index 91c5975fc8..4a9c7f9910 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -112,6 +112,10 @@ This repository contains three interconnected applications:
- Pay attention to available types and type guards in src/SIL.XForge.Scripture/ClientApp/src/type-utils.ts.
+# Tests
+
+- Place a line with "// SUT" before the line that causes the code being tested to be exercised.
+
# Running commands
- If you run frontend tests, run them in the `src/SIL.XForge.Scripture/ClientApp` directory with a command such as `npm run test:headless -- --watch=false --include '**/text.component.spec.ts' --include '**/settings.component.spec.ts'`
diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.html b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.html
index df01a2d9d7..7a60def055 100644
--- a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.html
+++ b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.html
@@ -48,6 +48,13 @@
+ @if (shouldShowDurationComparisonCaution) {
+
+ Be careful about comparing Serval build outcomes and durations before 2025-12-18 with those after, because of
+ changes to the event grouping system.
+
+ }
+
@if (!isLoading) {
@if (currentProjectFilter) {
diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.spec.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.spec.ts
index 3144953edf..8a9960edb9 100644
--- a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.spec.ts
+++ b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.spec.ts
@@ -650,6 +650,35 @@ describe('DraftJobsComponent', () => {
}));
});
+ describe('shouldShowDurationComparisonCaution', () => {
+ it('should return false if no date range is set', fakeAsync(() => {
+ const env = new TestEnvironment({ hasEvents: false });
+ env.wait();
+ env.component['currentDateRange'] = undefined;
+
+ // SUT
+ expect(env.component.shouldShowDurationComparisonCaution).toBeFalse();
+ }));
+
+ it('should return true when the date range starts before the cutoff', fakeAsync(() => {
+ const env = new TestEnvironment({ hasEvents: false });
+ env.wait();
+ env.setDateRange(new Date(2025, 12 - 1, 1), new Date(2026, 1 - 1, 20));
+
+ // SUT
+ expect(env.component.shouldShowDurationComparisonCaution).toBeTrue();
+ }));
+
+ it('should return false when the date range starts on or after the cutoff', fakeAsync(() => {
+ const env = new TestEnvironment({ hasEvents: false });
+ env.wait();
+ env.setDateRange(new Date(2026, 1 - 1, 20), new Date(2026, 2 - 1, 20));
+
+ // SUT
+ expect(env.component.shouldShowDurationComparisonCaution).toBeFalse();
+ }));
+ });
+
describe('formatDurationInHours', () => {
it('should format duration in decimal hours', fakeAsync(() => {
const env = new TestEnvironment({ hasEvents: false });
diff --git a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.ts b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.ts
index 05a6bfc256..5a95b5f581 100644
--- a/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.ts
+++ b/src/SIL.XForge.Scripture/ClientApp/src/app/serval-administration/draft-jobs.component.ts
@@ -210,6 +210,19 @@ export class DraftJobsComponent extends DataLoadingComponent implements OnInit {
return this.formatDurationInHours(this.maxDuration);
}
+ /** Whether to show a caution notice about comparing older durations. */
+ get shouldShowDurationComparisonCaution(): boolean {
+ if (this.currentDateRange == null) return false;
+
+ // Day after the date when draftGenerationRequestId began to be used, which was in SFv5.46.1 at
+ // 2025-12-18T17:48:57Z.
+ const requestIdIntroductionDate: Date = new Date(2025, 12 - 1, 18 + 1);
+
+ const rangeStartMs: number = this.currentDateRange.start.getTime();
+ const cutoffMs: number = requestIdIntroductionDate.getTime();
+ return rangeStartMs < cutoffMs;
+ }
+
ngOnInit(): void {
if (
!this.columnsToDisplay.includes('buildDetails') &&