Skip to content

Commit 1a156ba

Browse files
Marfuenclaude
andcommitted
fix(timelines): always reconcile after checkAutoCompletePhases
The reconcile call sat after an early return for phases.length === 0, which skipped it in the exact case it mattered: when all AUTO phases are already COMPLETED and a metric drops (regression). Wrap the advancement in a try/finally so reconciliation fires unconditionally on every event hook — extract advancement into runPhaseAdvancement for readability. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b9f400c commit 1a156ba

File tree

1 file changed

+25
-13
lines changed

1 file changed

+25
-13
lines changed

apps/api/src/frameworks/frameworks-timeline.helper.ts

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,31 @@ export async function checkAutoCompletePhases({
9797
}: {
9898
organizationId: string;
9999
timelinesService: TimelinesService;
100+
}) {
101+
try {
102+
await runPhaseAdvancement({ organizationId, timelinesService });
103+
} finally {
104+
// Always reconcile — handles regressions on COMPLETED phases, which
105+
// runPhaseAdvancement skips via its early return when no phase is
106+
// IN_PROGRESS. Without this, a metric drop on an already-completed
107+
// phase would be missed by every event hook.
108+
await timelinesService
109+
.reconcileAutoPhasesForOrganization(organizationId)
110+
.catch((err) =>
111+
logger.warn(
112+
'reconcileAutoPhasesForOrganization failed',
113+
err instanceof Error ? err.message : err,
114+
),
115+
);
116+
}
117+
}
118+
119+
async function runPhaseAdvancement({
120+
organizationId,
121+
timelinesService,
122+
}: {
123+
organizationId: string;
124+
timelinesService: TimelinesService;
100125
}) {
101126
const autoTypes = [
102127
PhaseCompletionType.AUTO_TASKS,
@@ -330,17 +355,4 @@ export async function checkAutoCompletePhases({
330355
);
331356
}
332357
}
333-
334-
// After per-phase completion attempts, run the full org-level
335-
// reconciliation so metric drops (regressions) also get picked up.
336-
// Without this, only event-driven advances would apply — regressions
337-
// previously happened in the GET /timelines read path, which is now pure.
338-
await timelinesService
339-
.reconcileAutoPhasesForOrganization(organizationId)
340-
.catch((err) =>
341-
logger.warn(
342-
'reconcileAutoPhasesForOrganization failed',
343-
err instanceof Error ? err.message : err,
344-
),
345-
);
346358
}

0 commit comments

Comments
 (0)