fix(cicd): expose reconcile timeline state summary

This commit is contained in:
Codex
2026-07-04 02:23:49 +00:00
parent bc681eddf8
commit 5e083e5fc6
4 changed files with 83 additions and 0 deletions
+1
View File
@@ -1982,6 +1982,7 @@ function mergeFollowerStatus(
message: live?.message ?? stringOrNull(stored.decision) ?? "no controller state yet",
timings: detailed ? timings : compactListTimings(timings),
reconcileTimeline: detailed ? reconcileTimeline : null,
rawStateDiagnostic: detailed ? asOptionalRecord(stored.rawStateDiagnostic) : null,
drilldown: `bun scripts/cli.ts cicd branch-follower status --follower ${follower.id} --live`,
};
if (!detailed) return summary;
+2
View File
@@ -241,6 +241,7 @@ function compactStateLike(value: Record<string, unknown> | null): Record<string,
totalSeconds: numberOrNull(value.totalSeconds),
timingStatus: stringOrNull(value.timingStatus),
resourceVersion: stringOrNull(metadata?.resourceVersion),
rawStateDiagnostic: asOptionalRecord(value.rawStateDiagnostic),
};
}
@@ -348,6 +349,7 @@ function stateSnapshot(read: K8sStateRead, followerId: string): Record<string, u
totalSeconds: numberOrNull(timings?.totalSeconds),
startedAt: stringOrNull(timings?.startedAt),
updatedAt: stringOrNull(state.updatedAt),
rawStateDiagnostic: asOptionalRecord(state.rawStateDiagnostic),
};
}
+16
View File
@@ -111,6 +111,7 @@ function renderStatusHuman(payload: Record<string, unknown>, _options: ParsedOpt
const errors = Array.isArray(payload.errors) ? payload.errors : [];
const timingRows = followers.flatMap(timingRowsForFollower).slice(0, 48);
const reconcileRows = followers.flatMap(reconcileRowsForFollower).slice(0, 48);
const rawStateRows = followers.flatMap(rawStateRowsForFollower).slice(0, 24);
return [
`CI/CD BRANCH-FOLLOWER STATUS (${payload.ok === false ? "degraded" : "ok"})`,
"",
@@ -122,6 +123,7 @@ function renderStatusHuman(payload: Record<string, unknown>, _options: ParsedOpt
table(["FOLLOWER", "PHASE", "ADAPTER", "OBSERVED", "TARGET", "TRIGGERED", "SUCCEEDED", "IN_FLIGHT", "BUDGET", "MESSAGE"], rows),
timingRows.length === 0 ? "" : `\nSTAGE TIMINGS\n${table(["FOLLOWER", "STAGE", "STATUS", "SECONDS", "BUDGET", "OBJECT"], timingRows)}`,
reconcileRows.length === 0 ? "" : `\nRECONCILE TIMELINE\n${table(["FOLLOWER", "STEP", "STATUS", "SECONDS", "STARTED", "OBJECT"], reconcileRows)}`,
rawStateRows.length === 0 ? "" : `\nRAW STATE DIAGNOSTIC\n${table(["FOLLOWER", "STATE_BYTES", "COMMAND", "TIMELINE", "STEPS", "TIMELINE_BYTES", "REASON"], rawStateRows)}`,
errors.length === 0 ? "" : `\nERRORS\n${errors.map((item) => `- ${item}`).join("\n")}`,
"",
"NEXT",
@@ -253,6 +255,20 @@ function reconcileRowsForTimeline(timeline: Record<string, unknown> | null, fall
]);
}
function rawStateRowsForFollower(item: Record<string, unknown>): unknown[][] {
const diagnostic = asOptionalRecord(item.rawStateDiagnostic);
if (diagnostic === null) return [];
return [[
item.id ?? "-",
diagnostic.valueBytes ?? "-",
diagnostic.hasCommand === true ? "yes" : "no",
diagnostic.hasReconcileTimeline === true ? "yes" : "no",
diagnostic.reconcileTimelineStepCount ?? "-",
diagnostic.reconcileTimelineBytes ?? "-",
stringOrNull(diagnostic.missingReason) ?? "-",
]];
}
function secondsFromMs(value: number | null): number | null {
return value === null ? null : Math.round(value / 100) / 10;
}