fix: bound web-probe and gh diagnostic output (#884)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
+34
-10
@@ -4711,6 +4711,22 @@ function compactCommentSummary(comment: GitHubComment): Record<string, unknown>
|
||||
};
|
||||
}
|
||||
|
||||
function compactIssueViewCommentSummary(comment: GitHubComment): Record<string, unknown> {
|
||||
const body = comment.body ?? "";
|
||||
return {
|
||||
id: comment.id,
|
||||
url: comment.html_url,
|
||||
author: comment.user?.login ?? null,
|
||||
createdAt: comment.created_at ?? null,
|
||||
updatedAt: comment.updated_at ?? null,
|
||||
bodyChars: body.length,
|
||||
bodySha: bodySha(body),
|
||||
bodyPreview: ghShort(preview(body).replace(/\s+/gu, " "), 200),
|
||||
bodyOmitted: true,
|
||||
fullBodyIncluded: false,
|
||||
};
|
||||
}
|
||||
|
||||
function prStateDetail(pr: GitHubPullRequest): "open" | "closed" | "merged" {
|
||||
if (pr.merged === true || pr.merged_at !== null && pr.merged_at !== undefined) return "merged";
|
||||
return pr.state === "closed" ? "closed" : "open";
|
||||
@@ -6019,13 +6035,13 @@ async function getIssueComment(token: string, repo: string, commentId: number):
|
||||
return githubRequest<GitHubComment>(token, "GET", `/repos/${owner}/${name}/issues/comments/${commentId}`);
|
||||
}
|
||||
|
||||
function selectedIssueJson(issue: GitHubIssue, comments: GitHubComment[] | null, fields: IssueViewJsonField[] | undefined): Record<string, unknown> | null {
|
||||
function selectedIssueJson(issue: GitHubIssue, comments: GitHubComment[] | null, fields: IssueViewJsonField[] | undefined, options: { includeFullCommentBodies?: boolean } = {}): Record<string, unknown> | null {
|
||||
if (fields === undefined) return null;
|
||||
const summary = issueSummary(issue);
|
||||
const selected: Record<string, unknown> = {};
|
||||
for (const field of fields) {
|
||||
if (field === "comments") {
|
||||
selected.comments = (comments ?? []).map(commentSummary);
|
||||
selected.comments = (comments ?? []).map(options.includeFullCommentBodies === true ? commentSummary : compactIssueViewCommentSummary);
|
||||
} else {
|
||||
selected[field] = summary[field];
|
||||
}
|
||||
@@ -6033,11 +6049,12 @@ function selectedIssueJson(issue: GitHubIssue, comments: GitHubComment[] | null,
|
||||
return selected;
|
||||
}
|
||||
|
||||
async function issueRead(repo: string, token: string, issueNumber: number, jsonFields: IssueViewJsonField[] | undefined, commandName = "issue read", disclosure: Record<string, unknown> | null = null): Promise<GitHubCommandResult> {
|
||||
async function issueRead(repo: string, token: string, issueNumber: number, jsonFields: IssueViewJsonField[] | undefined, commandName = "issue read", disclosure: Record<string, unknown> | null = null, options: { includeFullCommentBodies?: boolean } = {}): Promise<GitHubCommandResult> {
|
||||
const issue = await getIssue(token, repo, issueNumber);
|
||||
if (isGitHubError(issue)) return commandError(commandName, repo, issue, { issueNumber });
|
||||
const needsComments = jsonFields === undefined || jsonFields.includes("comments");
|
||||
const includeBody = jsonFields === undefined || jsonFields.includes("body");
|
||||
const includeFullCommentBodies = options.includeFullCommentBodies === true;
|
||||
const comments = needsComments ? await listIssueComments(token, repo, issueNumber) : null;
|
||||
if (isGitHubError(comments)) return commandError(commandName, repo, comments, { issueNumber, issue: issueSummary(issue, { includeBody, includePreview: false }) });
|
||||
return {
|
||||
@@ -6047,21 +6064,28 @@ async function issueRead(repo: string, token: string, issueNumber: number, jsonF
|
||||
...(disclosure === null ? {} : { disclosure }),
|
||||
issue: issueSummary(issue, { includeBody, includePreview: false }),
|
||||
codeQueueBoardHint: codeQueueBoardCommanderBriefHint(issueNumber, issue.body ?? ""),
|
||||
...(comments === null ? {} : { comments: comments.map(commentSummary) }),
|
||||
...(comments === null || jsonFields !== undefined ? {} : { comments: comments.map(includeFullCommentBodies ? commentSummary : compactCommentSummary) }),
|
||||
...(jsonFields === undefined ? {} : {
|
||||
jsonFields,
|
||||
json: selectedIssueJson(issue, comments, jsonFields),
|
||||
json: selectedIssueJson(issue, comments, jsonFields, { includeFullCommentBodies }),
|
||||
compatibility: {
|
||||
legacyJsonBodyPath: includeBody ? ".data.issue.body" : null,
|
||||
bodyOmitted: !includeBody,
|
||||
readCommands: includeBody ? null : issueBodyReadCommands(repo, issueNumber),
|
||||
commentsCompacted: comments !== null && !includeFullCommentBodies,
|
||||
commentBodiesOmitted: comments !== null && !includeFullCommentBodies,
|
||||
fullCommentBodiesIncluded: comments !== null && includeFullCommentBodies,
|
||||
commentsPath: comments === null ? null : ".data.json.comments",
|
||||
readCommands: {
|
||||
...(includeBody ? {} : issueBodyReadCommands(repo, issueNumber)),
|
||||
...(comments !== null && !includeFullCommentBodies ? issueCommentReadCommands(repo, issueNumber) : {}),
|
||||
},
|
||||
},
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
async function issueView(repo: string, token: string, issueNumber: number, jsonFields: IssueViewJsonField[] | undefined, disclosure: Record<string, unknown> | null = null): Promise<GitHubCommandResult> {
|
||||
return issueRead(repo, token, issueNumber, jsonFields, "issue view", disclosure);
|
||||
async function issueView(repo: string, token: string, issueNumber: number, jsonFields: IssueViewJsonField[] | undefined, disclosure: Record<string, unknown> | null = null, options: { includeFullCommentBodies?: boolean } = {}): Promise<GitHubCommandResult> {
|
||||
return issueRead(repo, token, issueNumber, jsonFields, "issue view", disclosure, options);
|
||||
}
|
||||
|
||||
const GITHUB_USER_ATTACHMENT_URL_PATTERN = /https:\/\/github\.com\/user-attachments\/assets\/[A-Za-z0-9._-]+(?:\?[^\s<>"')]+)?/giu;
|
||||
@@ -8653,8 +8677,8 @@ export async function runGhCommand(args: string[]): Promise<GitHubCommandResult
|
||||
const missing = authRequired(resolved.repo, `issue ${sub}`, probe);
|
||||
if (missing !== null || token === null) return missing ?? authRequired(resolved.repo, `issue ${sub}`, { present: false, source: null, ghFallbackAttempted: true });
|
||||
const disclosure = readDisclosureOptions(options, resolved.shorthand);
|
||||
if (sub === "read") return withNumberOptionHint(issueRead(resolved.repo, token, resolved.number, issueReadJsonFields(options), "issue read", disclosure), resolved);
|
||||
return withNumberOptionHint(issueView(resolved.repo, token, resolved.number, issueReadJsonFields(options), disclosure), resolved);
|
||||
if (sub === "read") return withNumberOptionHint(issueRead(resolved.repo, token, resolved.number, issueReadJsonFields(options), "issue read", disclosure, { includeFullCommentBodies: options.raw || options.full }), resolved);
|
||||
return withNumberOptionHint(issueView(resolved.repo, token, resolved.number, issueReadJsonFields(options), disclosure, { includeFullCommentBodies: options.raw || options.full }), resolved);
|
||||
}
|
||||
const { token, probe } = resolveToken(true);
|
||||
const missing = authRequired(options.repo, `issue ${sub ?? ""}`.trim(), probe);
|
||||
|
||||
@@ -403,7 +403,9 @@ async function runNodeDelegatedDomain(config: Config, domain: DelegatedNodeDomai
|
||||
if (domain === "control-plane" && scoped.node !== defaultSpec.nodeId) {
|
||||
if (scoped.action === "status") {
|
||||
const result = nodeRuntimeControlPlaneStatus(scoped);
|
||||
return nodeScopedFullOutput(scoped) ? result : withNodeRuntimeControlPlaneStatusRendered(result, scoped);
|
||||
if (scoped.originalArgs.includes("--raw")) return result;
|
||||
if (scoped.originalArgs.includes("--full")) return withNodeRuntimeControlPlaneStatusFullRendered(result, scoped);
|
||||
return withNodeRuntimeControlPlaneStatusRendered(result, scoped);
|
||||
}
|
||||
if (scoped.action === "apply" || scoped.action === "trigger-current" || scoped.action === "refresh" || scoped.action === "sync" || scoped.action === "runtime-migration" || scoped.action === "cleanup-runs") {
|
||||
if (scoped.confirm && !scoped.dryRun && !scoped.wait) return startNodeDelegatedJob(scoped);
|
||||
@@ -3197,6 +3199,101 @@ function withNodeRuntimeControlPlaneStatusRendered(result: Record<string, unknow
|
||||
return { ...result, renderedText, contentType: "text/plain" };
|
||||
}
|
||||
|
||||
function withNodeRuntimeControlPlaneStatusFullRendered(result: Record<string, unknown>, scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>): RenderedCliResult {
|
||||
const summary = record(result.summary);
|
||||
const pipelineRun = record(summary.pipelineRun);
|
||||
const argo = record(summary.argo);
|
||||
const runtimeSummary = record(summary.runtime);
|
||||
const publicProbe = record(summary.publicProbe);
|
||||
const gitMirror = record(summary.gitMirror);
|
||||
const runtime = record(result.runtime);
|
||||
const bridge = record(runtime.externalPostgresBridge);
|
||||
const secrets = record(runtime.externalPostgresSecrets);
|
||||
const next = record(summary.next);
|
||||
const workloadReadiness = Array.isArray(runtime.workloadReadiness) ? runtime.workloadReadiness.map(record) : [];
|
||||
const workloadRows = workloadReadiness.length === 0
|
||||
? [["-", "-", "-", "-", "-"]]
|
||||
: workloadReadiness.slice(0, 18).map((item) => [
|
||||
webObserveText(item.ref),
|
||||
item.ready === true ? "ok" : "not-ready",
|
||||
webObserveText(item.readyReplicas),
|
||||
webObserveText(item.currentReplicas),
|
||||
webObserveText(item.desiredReplicas),
|
||||
]);
|
||||
const bridgeRows = bridge.required === false
|
||||
? [["not-required", "-", "-", "-", "-"]]
|
||||
: [[
|
||||
bridge.ready === true ? "ok" : "failed",
|
||||
webObserveText(bridge.serviceName),
|
||||
webObserveText(bridge.endpointAddress),
|
||||
webObserveText(bridge.port),
|
||||
`${webObserveText(bridge.expectedEndpointAddress)}:${webObserveText(bridge.expectedPort)}`,
|
||||
]];
|
||||
const secretRows = nodeRuntimeExternalPostgresSecretRows(secrets);
|
||||
const status = result.ok === true ? "ok" : result.ok === false ? "failed" : "-";
|
||||
const renderedText = [
|
||||
"hwlab nodes control-plane status --full",
|
||||
"",
|
||||
webObserveTable(
|
||||
["NODE", "LANE", "STATUS", "REASON", "SOURCE", "PIPELINERUN"],
|
||||
[[
|
||||
scoped.node,
|
||||
scoped.lane,
|
||||
status,
|
||||
webObserveShort(webObserveText(summary.degradedReason ?? result.degradedReason), 40),
|
||||
shortValue(summary.sourceCommit ?? result.sourceCommit),
|
||||
webObserveShort(webObserveText(pipelineRun.name ?? result.pipelineRun), 36),
|
||||
]],
|
||||
),
|
||||
"",
|
||||
webObserveTable(
|
||||
["STAGE", "STATUS", "DETAIL"],
|
||||
[
|
||||
["pipeline", pipelineRun.ready === true ? "ok" : pipelineRun.exists === true ? webObserveText(pipelineRun.status) : "missing", webObserveShort(webObserveText(pipelineRun.reason ?? pipelineRun.message), 80)],
|
||||
["argo", argo.ready === true ? "ok" : "failed", `${webObserveText(argo.syncStatus)}/${webObserveText(argo.health)} rev=${shortValue(argo.syncRevision)}`],
|
||||
["runtime", runtimeSummary.ready === true ? "ok" : "failed", `namespace=${webObserveText(runtimeSummary.namespace)} workloads=${webObserveText(runtimeSummary.workloadReady)} pg=${webObserveText(runtimeSummary.externalPostgresReady ?? runtimeSummary.localPostgresReady)}`],
|
||||
["public", publicProbe.ready === true ? "ok" : "failed", webObserveShort(webObserveText(record(publicProbe.diagnostic).kind ?? record(publicProbe.diagnostic).message), 80)],
|
||||
["git-mirror", gitMirror.ready === true ? "ok" : "failed", `pending=${webObserveText(gitMirror.pendingFlush)} inSync=${webObserveText(gitMirror.githubInSync)}`],
|
||||
],
|
||||
),
|
||||
"",
|
||||
"RUNTIME_WORKLOADS",
|
||||
webObserveTable(["REF", "READY", "READY_REPLICAS", "CURRENT", "DESIRED"], workloadRows),
|
||||
...(workloadReadiness.length > workloadRows.length ? [` ... ${workloadReadiness.length - workloadRows.length} more workloads omitted; use --raw for complete JSON.`] : []),
|
||||
"",
|
||||
"EXTERNAL_POSTGRES_BRIDGE",
|
||||
webObserveTable(["STATUS", "SERVICE", "ENDPOINT", "PORT", "EXPECTED"], bridgeRows),
|
||||
"",
|
||||
"EXTERNAL_POSTGRES_SECRETS",
|
||||
webObserveTable(["ROLE", "SECRET", "KEY", "PRESENT", "VALUES_PRINTED"], secretRows),
|
||||
"",
|
||||
"NEXT",
|
||||
` ${summary.nextAction ?? next.full ?? `bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane} --full`}`,
|
||||
` bun scripts/cli.ts hwlab nodes control-plane status --node ${scoped.node} --lane ${scoped.lane} --raw`,
|
||||
"",
|
||||
"Disclosure:",
|
||||
" --full status is a bounded runtime-workloads drill-down view; use --raw only when complete JSON is required.",
|
||||
" Secret evidence is limited to object metadata, key names, presence, and valuesPrinted=false.",
|
||||
].join("\n");
|
||||
return { ...result, renderedText, contentType: "text/plain" };
|
||||
}
|
||||
|
||||
function nodeRuntimeExternalPostgresSecretRows(secrets: Record<string, unknown>): unknown[][] {
|
||||
if (secrets.required === false) return [["not-required", "-", "-", "-", "false"]];
|
||||
if (secrets.required === true && secrets.ready !== true && secrets.degradedReason !== undefined) {
|
||||
return [[webObserveText(secrets.degradedReason), "-", "-", "false", "false"]];
|
||||
}
|
||||
const cloudApi = record(secrets.cloudApi);
|
||||
const openfga = record(secrets.openfga);
|
||||
const datastoreUri = record(openfga.datastoreUri);
|
||||
const authnPresharedKey = record(openfga.authnPresharedKey);
|
||||
return [
|
||||
["cloudApi", webObserveText(cloudApi.secret), webObserveText(cloudApi.key), webObserveText(cloudApi.present), webObserveText(cloudApi.valuesPrinted ?? false)],
|
||||
["openfga.datastoreUri", webObserveText(datastoreUri.secret), webObserveText(datastoreUri.key), webObserveText(datastoreUri.present), webObserveText(datastoreUri.valuesPrinted ?? false)],
|
||||
["openfga.authnPresharedKey", webObserveText(authnPresharedKey.secret), webObserveText(authnPresharedKey.key), webObserveText(authnPresharedKey.present), webObserveText(authnPresharedKey.valuesPrinted ?? false)],
|
||||
];
|
||||
}
|
||||
|
||||
function nodeRuntimeGitMirrorStatus(scoped: ReturnType<typeof parseNodeScopedDelegatedOptions>): Record<string, unknown> {
|
||||
const spec = scoped.spec;
|
||||
const mirror = nodeRuntimeGitMirrorTarget(spec);
|
||||
|
||||
@@ -1402,7 +1402,7 @@ function buildFindings(samples, control, network, errors, sampleMetrics, promptN
|
||||
if (transientCrossPageProjectionDiffs.length > 0) findings.push({ id: "cross-page-projection-transient-divergence", severity: "info", summary: "control and observer pages briefly differed near a sampled transition; retained as transient evidence but not treated as persistent projection failure", count: transientCrossPageProjectionDiffs.length, budgetMs: crossPageProjectionBudgetMs, samples: transientCrossPageProjectionDiffs.slice(0, 20) });
|
||||
if (crossPageTraceVisibilityDiffs.length > 0) findings.push({ id: "cross-page-trace-visibility-divergence", severity: "info", summary: "control and observer pages differed only in visible trace row count; this is local disclosure/hydration visibility, not session/message projection divergence", count: crossPageTraceVisibilityDiffs.length, samples: crossPageTraceVisibilityDiffs.slice(0, 20) });
|
||||
const traceMessageDuplicates = detectTraceMessageDuplication(samples);
|
||||
if (traceMessageDuplicates.length > 0) findings.push({ id: "trace-assistant-message-duplicates-final-response", severity: "amber", summary: "trace rendered assistant message rows that duplicate the sealed final response", count: traceMessageDuplicates.length, samples: traceMessageDuplicates.slice(0, 20) });
|
||||
if (traceMessageDuplicates.length > 0) findings.push({ id: "trace-assistant-message-duplicates-final-response", severity: "amber", summary: "trace-frame rendered duplicate visible assistant final rows; the fixed Final Response renderer summary block is excluded", count: traceMessageDuplicates.length, finalResponseSummaryBlockCounted: false, traceFrameSource: "traceRows-only", samples: traceMessageDuplicates.slice(0, 20) });
|
||||
const turnTraceMissing = detectTurnTraceIdMissing(samples);
|
||||
if (turnTraceMissing.length > 0) findings.push({ id: "turn-trace-id-missing", severity: "red", summary: "Code Agent turn/card was visible without a trace id, so historical trace hydration cannot be reliable", count: turnTraceMissing.length, samples: turnTraceMissing.slice(0, 20) });
|
||||
const pagePerformanceItems = Array.isArray(pagePerformance?.sameOriginApiByPath) ? pagePerformance.sameOriginApiByPath : [];
|
||||
|
||||
@@ -1897,17 +1897,32 @@ function detectAdjacentCrossPageProjectionDiffs(samples) {
|
||||
function detectTraceMessageDuplication(samples) {
|
||||
const rows = [];
|
||||
for (const sample of samples) {
|
||||
const finalText = normalizedText((Array.isArray(sample?.messages) ? sample.messages : []).map((item) => item?.textPreview || item?.text || "").join("\n"));
|
||||
if (finalText.length < 40) continue;
|
||||
const traceRows = Array.isArray(sample?.traceRows) ? sample.traceRows : [];
|
||||
for (const row of traceRows) {
|
||||
const groups = new Map();
|
||||
for (let fallbackIndex = 0; fallbackIndex < traceRows.length; fallbackIndex += 1) {
|
||||
const row = traceRows[fallbackIndex];
|
||||
const rowTextRaw = String(row?.textPreview || row?.text || "");
|
||||
if (!/(?:助手消息|assistant\s+message|assistant)/iu.test(rowTextRaw)) continue;
|
||||
const rowText = normalizedText(rowTextRaw);
|
||||
if (rowText.length < 24) continue;
|
||||
const overlap = longestSharedSubstringLength(finalText, rowText);
|
||||
if (overlap < 40 && overlap < Math.min(rowText.length, finalText.length) * 0.55) continue;
|
||||
rows.push({ ...ref(sample), traceId: row?.traceId ?? null, rowIndex: row?.index ?? null, rowTextPreview: limitText(rowTextRaw, 180) });
|
||||
const traceId = row?.traceId === undefined || row?.traceId === null ? "" : String(row.traceId);
|
||||
const key = traceId + "\u0000" + rowText;
|
||||
const group = groups.get(key) ?? { traceId: traceId || null, rowText, rowTextRaw, rows: [] };
|
||||
group.rows.push({ row, fallbackIndex });
|
||||
groups.set(key, group);
|
||||
}
|
||||
for (const group of groups.values()) {
|
||||
if (group.rows.length < 2) continue;
|
||||
rows.push({
|
||||
...ref(sample),
|
||||
traceId: group.traceId,
|
||||
visibleAssistantFinalRowCount: group.rows.length,
|
||||
rowIndexes: group.rows.map((item) => item.row?.index ?? item.fallbackIndex).slice(0, 12),
|
||||
rowTextHash: sha256(group.rowText),
|
||||
rowTextPreview: limitText(group.rowTextRaw, 180),
|
||||
finalResponseSummaryBlockCounted: false,
|
||||
traceFrameSource: "traceRows-only"
|
||||
});
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
|
||||
Reference in New Issue
Block a user