cicd recover follower total timing from native state

This commit is contained in:
Codex
2026-07-03 11:35:12 +00:00
parent 8557ab3553
commit c9824a5847
2 changed files with 22 additions and 2 deletions
@@ -66,6 +66,8 @@ Stage timing must be queryable through normal CLI output, not only raw JSON. `st
`timings.totalSeconds` is the authoritative end-to-end wall-clock measurement for a triggered run: measure from `timings.startedAt` until `timings.finishedAt`, or until query time while closeout is still running. Do not compute total by summing stage rows, because stage rows can overlap, omit external waiting, or be reported by different native objects.
If a controller loop resumes an already-triggered source change and the stored state predates `startedAt`, it may recover the total timer from the native PipelineRun start time and then persist that recovered start point.
State machine phases are `Observed`, `Noop`, `PendingTrigger`, `Triggering`, `ClosingOut`, `Succeeded`, `Failed`, `Superseded`, `Blocked`, and `Skipped`.
Status and decision inputs are Kubernetes-native:
+20 -2
View File
@@ -2086,10 +2086,11 @@ function buildFollowerTimings(
storedTimings?: Record<string, unknown> | null,
phase?: BranchFollowerPhase,
): FollowerState["timings"] {
const total = totalTimingFromCommand(triggerCommand, phase) ?? totalTimingFromStored(storedTimings, phase);
const nativePayload = asOptionalRecord(live.payload);
const total = totalTimingFromCommand(triggerCommand, phase) ?? totalTimingFromStored(storedTimings, phase) ?? totalTimingFromLivePayload(nativePayload, phase);
const stages = dedupeTimingStages([
...stageTimingsFromCommand(triggerCommand),
...stageTimingsFromNativePayload(asOptionalRecord(live.payload)),
...stageTimingsFromNativePayload(nativePayload),
]).slice(0, 24);
return {
budgetSeconds: follower.budgets.endToEndSeconds,
@@ -2162,6 +2163,23 @@ function totalTimingFromStored(storedTimings: Record<string, unknown> | null | u
};
}
function totalTimingFromLivePayload(payload: Record<string, unknown> | null, phase?: BranchFollowerPhase): { seconds: number; status: string; source: string; startedAt: string | null; finishedAt: string | null } | null {
if (payload === null) return null;
const tekton = asOptionalRecord(payload.tekton);
const startedAt = stringOrNull(tekton?.startTime);
if (startedAt === null) return null;
const finishedAt = phase !== undefined && terminalPhase(phase) ? new Date().toISOString() : null;
const seconds = totalSecondsFromRange(startedAt, finishedAt);
if (seconds === null) return null;
return {
seconds,
status: phase === undefined ? "native-observed" : phase.toLowerCase(),
source: "tekton-pipelinerun-start",
startedAt,
finishedAt,
};
}
function totalSecondsFromRange(startedAt: string | null, finishedAt: string | null): number | null {
const startedMs = timestampMs(startedAt);
if (startedMs === null) return null;