Merge pull request #1231 from pikasTech/fix/1213-workbench-timing-source

修复 Workbench timing source-of-truth 可见性
This commit is contained in:
Lyon
2026-06-28 19:26:00 +08:00
committed by GitHub
6 changed files with 172 additions and 31 deletions
+45 -2
View File
@@ -2903,6 +2903,7 @@ function runSentinelQuickVerify(state: SentinelCicdState, reason: string, timeou
status: "blocked",
observerId: null,
elapsedMs: 0,
businessStatus: quickVerifyBusinessStatus("quick-verify-account-secret-missing", 0, null, null, 0, maxSeconds),
steps: [{ phase: "quick-verify-account-env", ok: false, result: accountEnv.summary }],
failure: "quick-verify-account-secret-missing",
findingCount: findings.length,
@@ -2951,6 +2952,7 @@ function runSentinelQuickVerify(state: SentinelCicdState, reason: string, timeou
status: "blocked",
observerId,
elapsedMs: elapsedMs(),
businessStatus: quickVerifyBusinessStatus("observe-start-failed", 0, null, null, elapsedMs(), maxSeconds),
steps,
failure: "observe-start-failed",
findingCount: findings.length,
@@ -3118,6 +3120,7 @@ function runSentinelQuickVerify(state: SentinelCicdState, reason: string, timeou
const blockingFindings = findings.filter(isQuickVerifyBlockingFinding);
const analysisWarnings = analysis.ok ? [] : ["quick verify analyze command returned non-zero but a readable analysis artifact was produced; targetValidation is using artifact severity plus control blockers."];
const ok = record(artifactSummary).ok === true && controlFindings.length === 0 && blockingFindings.length === 0;
const businessStatus = quickVerifyBusinessStatus(null, promptIndex, turnSummary, traceFrame, elapsedMs(), maxSeconds);
printQuickVerifyProgress(state, runId, "record-report", ok ? "succeeded" : "blocked", { observerId, reportJsonSha256: stringAtNullable(artifactSummary, "reportJsonSha256"), findingCount: findings.length, blockingFindingCount: blockingFindings.length, controlFindingCount: controlFindings.length, elapsedMs: elapsedMs() });
return recordQuickVerify(state, {
ok,
@@ -3127,6 +3130,7 @@ function runSentinelQuickVerify(state: SentinelCicdState, reason: string, timeou
status: ok ? "analyzed" : "blocked",
observerId,
elapsedMs: elapsedMs(),
businessStatus,
stateDir: indexEntry?.stateDir ?? null,
reportJsonSha256: stringAtNullable(artifactSummary, "reportJsonSha256"),
findingCount: findings.length,
@@ -3308,6 +3312,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
&& record(artifactSummary).ok === true
&& controlFindings.length === 0
&& blockingFindings.length === 0;
const businessStatus = quickVerifyBusinessStatus(input.failure, input.promptIndex, turnSummary, traceFrame, input.elapsedMs ?? null, numberAt(state.cicd, "targetValidation.maxSeconds"));
return {
ok: recoveredWaitFailure,
runId: input.runId,
@@ -3316,6 +3321,7 @@ function finalizeQuickVerifyFailure(state: SentinelCicdState, input: {
status: recoveredWaitFailure ? "analyzed" : "blocked",
observerId: input.observerId,
elapsedMs: input.elapsedMs ?? null,
businessStatus,
stateDir: indexEntry?.stateDir ?? null,
reportJsonSha256: stringAtNullable(artifactSummary, "reportJsonSha256"),
findingCount: findings.length,
@@ -3347,6 +3353,7 @@ function recordQuickVerify(state: SentinelCicdState, payload: Record<string, unk
const summary = {
reason: payload.reason,
status: payload.status,
businessStatus: payload.businessStatus ?? null,
elapsedMs: payload.elapsedMs,
failure: payload.failure,
warnings: Array.isArray(payload.warnings) ? payload.warnings : [],
@@ -3365,6 +3372,7 @@ function recordQuickVerify(state: SentinelCicdState, payload: Record<string, unk
findingCount: payload.findingCount,
artifactCount: payload.artifactCount,
summary,
businessStatus: payload.businessStatus ?? null,
findings: payload.findings,
views,
screenshot: payload.screenshot,
@@ -4898,6 +4906,38 @@ function quickVerifyHasDurableBusinessTurn(promptIndex: number, turnSummary: Rec
&& !/\s*trace\s*rows|no\s+trace\s+rows|traceId=-|routeSession=-|activeSession=-/iu.test(renderedTrace);
}
function quickVerifyBusinessStatus(
failure: string | null,
promptIndex: number,
turnSummary: Record<string, unknown> | null,
traceFrame: Record<string, unknown> | null,
elapsedMs: unknown,
budgetSeconds: number,
): Record<string, unknown> {
const durableBusinessTurn = quickVerifyHasDurableBusinessTurn(promptIndex, turnSummary, traceFrame);
const elapsed = typeof elapsedMs === "number" && Number.isFinite(elapsedMs) ? elapsedMs : null;
const budgetMs = Math.max(0, budgetSeconds) * 1000;
const budgetExceeded = elapsed !== null && budgetMs > 0 && elapsed > budgetMs;
const observerTimeout = budgetExceeded || (failure !== null && isRecoverableQuickVerifyWaitFailure(failure));
const status = durableBusinessTurn
? "business-turn-completed"
: observerTimeout
? "observer-timeout"
: "scenario-incomplete";
return {
status,
durableBusinessTurn,
observerTimeout,
scenarioComplete: durableBusinessTurn,
failure: failure ?? null,
promptIndex,
elapsedMs: elapsed,
budgetSeconds,
sourceOfTruth: durableBusinessTurn ? "turn-summary-or-trace-frame" : observerTimeout ? "runner-wait-budget" : "control-findings",
valuesRedacted: true,
};
}
function isRecoverableQuickVerifyWaitFailure(failure: string): boolean {
return failure === "quick-verify-wait-chunk-timeout"
|| failure === "quick-verify-timeout-over-budget"
@@ -4969,6 +5009,7 @@ function renderMaintenanceResult(result: Record<string, unknown>): string {
const serviceHealth = record(result.serviceHealth);
const maintenance = record(result.maintenance);
const quickVerify = record(result.quickVerify);
const quickVerifyBusiness = record(quickVerify.businessStatus);
const planned = record(result.planned);
const blocker = record(result.blocker);
const next = record(result.next);
@@ -4987,7 +5028,7 @@ function renderMaintenanceResult(result: Record<string, unknown>): string {
? table(["ACTION", "RELEASE", "REASON", "QUICK_VERIFY"], [[planned.action, planned.releaseId, planned.reason, planned.quickVerify]])
: "MAINTENANCE\n-",
"",
Object.keys(quickVerify).length === 0 ? "QUICK_VERIFY\n-" : table(["OK", "RUN", "SCENARIO", "OBSERVER", "REPORT", "FINDINGS"], [[quickVerify.ok, quickVerify.runId, quickVerify.scenarioId, quickVerify.observerId, quickVerify.reportJsonSha256, quickVerify.findingCount]]),
Object.keys(quickVerify).length === 0 ? "QUICK_VERIFY\n-" : table(["OK", "BUSINESS", "RUN", "SCENARIO", "OBSERVER", "REPORT", "FINDINGS"], [[quickVerify.ok, quickVerifyBusiness.status ?? "-", quickVerify.runId, quickVerify.scenarioId, quickVerify.observerId, quickVerify.reportJsonSha256, quickVerify.findingCount]]),
"",
Object.keys(blocker).length === 0 ? "BLOCKER\n-" : table(["CODE", "REASON"], [[blocker.code, blocker.reason]]),
"",
@@ -5269,6 +5310,7 @@ function renderControlPlaneResult(result: Record<string, unknown>): string {
const argoApply = record(result.argoApply);
const blocker = record(result.blocker);
const targetValidation = record(result.targetValidation);
const targetValidationBusiness = record(targetValidation.businessStatus);
const next = record(result.next);
const warnings = Array.isArray(result.warnings) ? result.warnings : [];
return [
@@ -5286,9 +5328,10 @@ function renderControlPlaneResult(result: Record<string, unknown>): string {
"",
Object.keys(sourceMirrorSync).length === 0 ? "SOURCE_MIRROR_SYNC\n-" : table(["OK", "PHASE", "JOB", "COMMIT", "ELAPSED"], [[sourceMirrorSync.ok, sourceMirrorSync.phase, sourceMirrorSync.jobName, short(record(sourceMirrorSync.payload).mirrorCommit), sourceMirrorSync.elapsedMs ?? "-"]]),
"",
Object.keys(targetValidation).length === 0 ? "TARGET_VALIDATION\n-" : table(["OK", "STATUS", "SCENARIO", "RUN", "OBSERVER", "REPORT", "FINDINGS", "ARTIFACTS"], [[
Object.keys(targetValidation).length === 0 ? "TARGET_VALIDATION\n-" : table(["OK", "STATUS", "BUSINESS", "SCENARIO", "RUN", "OBSERVER", "REPORT", "FINDINGS", "ARTIFACTS"], [[
targetValidation.ok,
targetValidation.status,
targetValidationBusiness.status ?? "-",
targetValidation.scenarioId,
targetValidation.runId,
targetValidation.observerId,