Merge pull request #1331 from pikasTech/fix/1297-check7056-periodic

fix: unblock JD01 sentinel quick-verify observe start
This commit is contained in:
Lyon
2026-07-01 03:10:53 +08:00
committed by GitHub
3 changed files with 41 additions and 7 deletions
@@ -106,7 +106,7 @@ baselines:
jd01BootstrapSource: &jd01-bootstrap-source
purpose: bootstrap-admin
sourceRef: .env/HWLAB_admin.txt
sourceKey: HWLAB_BOOTSTRAP_ADMIN_PASSWORD
sourceKey: HWLAB_ADMIN_PASSWORD
sourceLine: 2
dsflashPromptSource: &dsflash-prompt-source
purpose: prompt-set
@@ -124,7 +124,8 @@ export function runSentinelQuickVerify(state: SentinelCicdState, reason: string,
const observerId = observerIdFromText(String(record(started.result).stdoutPreview ?? ""));
printQuickVerifyProgress(state, runId, "observe-start", started.ok && observerId !== null ? "succeeded" : "failed", { observerId, exitCode: record(started.result).exitCode ?? null, timedOut: record(started.result).timedOut === true, elapsedMs: elapsedMs() });
if (!started.ok || observerId === null) {
const findings = quickVerifyControlFindings("observe-start-failed", 0, null, null);
const findings = quickVerifyControlFindings("observe-start-failed", 0, null, null)
.map((finding) => enrichObserveStartFailureFinding(finding, record(started.result)));
return recordQuickVerify(state, {
ok: false,
runId,
@@ -789,6 +790,8 @@ function compactQuickVerifyRecordStepResult(value: Record<string, unknown>): Rec
stderrBytes: numberAtNullable(value, "stderrBytes"),
stdoutPreview: boundQuickVerifyRecordText(value.stdoutPreview, 240),
stderrPreview: boundQuickVerifyRecordText(value.stderrPreview, 240),
stdoutTail: boundQuickVerifyRecordText(value.stdoutTail, 1200),
stderrTail: boundQuickVerifyRecordText(value.stderrTail, 1200),
valuesRedacted: true,
};
}
@@ -1428,6 +1431,32 @@ function quickVerifyControlFindings(failure: string | null, promptIndex: number,
}];
}
function enrichObserveStartFailureFinding(finding: Record<string, unknown>, result: Record<string, unknown>): Record<string, unknown> {
const stdout = stringAtNullable(result, "stdoutPreview") ?? stringAtNullable(result, "stdoutTail");
const payload = stdout === null ? null : parseJsonObject(stdout);
const data = record(record(payload).data);
const degradedReason = stringAtNullable(data, "degradedReason");
const command = stringAtNullable(data, "command");
if (degradedReason === null) {
return {
...finding,
evidenceSummary: `observe start exited ${String(result.exitCode ?? "-")}; timedOut=${result.timedOut === true}`,
valuesRedacted: true,
};
}
return {
...finding,
rootCause: `observe-start failed: ${degradedReason}`,
rootCauseStatus: "confirmed",
rootCauseConfidence: "high",
nextAction: degradedReason === "web_login_secret_missing"
? "Align YAML-declared bootstrap secret env injection with web-probe observe credential materialization."
: "Inspect observe-start failure payload and fix the reported degradedReason.",
evidenceSummary: `${command ?? "web-probe observe start"} degradedReason=${degradedReason}`,
valuesRedacted: true,
};
}
function quickVerifyCompletedTurnSummaryRow(promptIndex: number, turnSummary: Record<string, unknown> | null): Record<string, unknown> | null {
const rows = Array.isArray(record(turnSummary?.collect).rows) ? record(turnSummary?.collect).rows.map(record) : [];
const scopedRows = promptIndex > 0 ? rows.filter((row) => numberAtNullable(row, "round") === promptIndex) : rows;
+10 -5
View File
@@ -1336,7 +1336,7 @@ export function readBootstrapAdminSecretMaterial(spec: RuntimeSecretSpec): Boots
sourceKey,
sourceLine,
sourcePath: password.sourcePath,
sourcePresent: true,
sourcePresent: password.sourcePresent,
sourceFingerprint: shortSecretFingerprint(password.value),
passwordHash: hwlabPasswordHash(password.value),
error: null,
@@ -1365,7 +1365,7 @@ export function readBootstrapAdminPasswordMaterial(spec: RuntimeSecretSpec): Boo
sourceKey,
sourceLine,
sourcePath: password.sourcePath,
sourcePresent: true,
sourcePresent: password.sourcePresent,
sourceFingerprint: shortSecretFingerprint(password.value),
password: password.value,
error: null,
@@ -1377,22 +1377,27 @@ function readBootstrapAdminUsername(spec: RuntimeSecretSpec): { ok: true; value:
if (ref === undefined) return { ok: true, value: spec.bootstrapAdminUsername };
const key = spec.bootstrapAdminUsernameSourceKey ?? "username";
const material = readSecretSourceScalar(ref, key, spec.bootstrapAdminUsernameSourceLine ?? null);
if (!material.ok && material.error === "secret-source-missing" && spec.bootstrapAdminUsername.length > 0) return { ok: true, value: spec.bootstrapAdminUsername };
if (!material.ok) return { ok: false, error: `username-${material.error}` };
return { ok: true, value: material.value };
}
function readSecretSourceScalar(sourceRef: string, sourceKey: string, sourceLine: number | null): { ok: true; value: string; sourcePath: string; sourcePresent: true } | { ok: false; sourcePath: string; sourcePresent: boolean; error: string } {
function readSecretSourceScalar(sourceRef: string, sourceKey: string, sourceLine: number | null): { ok: true; value: string; sourcePath: string; sourcePresent: boolean } | { ok: false; sourcePath: string; sourcePresent: boolean; error: string } {
const paths = secretSourcePaths(sourceRef);
const sourcePath = paths.find((candidate) => existsSync(candidate)) ?? paths[0] ?? join(repoRoot, ".state", "secrets", sourceRef);
if (!existsSync(sourcePath)) return { ok: false, sourcePath, sourcePresent: false, error: "secret-source-missing" };
const runtimeValue = process.env[sourceKey];
if (!existsSync(sourcePath)) {
if (runtimeValue !== undefined && runtimeValue.length > 0) return { ok: true, value: runtimeValue, sourcePath, sourcePresent: false };
return { ok: false, sourcePath, sourcePresent: false, error: "secret-source-missing" };
}
const text = readFileSync(sourcePath, "utf8");
if (sourceLine !== null) {
const line = text.split(/\r?\n/u)[sourceLine - 1]?.replace(/\r$/u, "") ?? "";
if (line.length === 0 && runtimeValue !== undefined && runtimeValue.length > 0) return { ok: true, value: runtimeValue, sourcePath, sourcePresent: true };
if (line.length === 0) return { ok: false, sourcePath, sourcePresent: true, error: "secret-line-missing" };
return { ok: true, value: line, sourcePath, sourcePresent: true };
}
const values = parseEnvFile(text);
const runtimeValue = process.env[sourceKey];
const value = values[sourceKey] ?? runtimeValue;
if (value === undefined || value.length === 0) return { ok: false, sourcePath, sourcePresent: true, error: "secret-key-missing" };
return { ok: true, value, sourcePath, sourcePresent: true };