mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 14:20:35 +08:00
fix(authority): classify capability probe failures
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -162,6 +162,26 @@ function readPayloadMessage(payload = {}, fallback = "") {
|
|||||||
return String(payload.error || payload.message || payload.reason || fallback || "");
|
return String(payload.error || payload.message || payload.reason || fallback || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function classifyAuthorityProbeStatus(status = 0, payload = null) {
|
||||||
|
const payloadCategory = String(payload?.category || "").trim();
|
||||||
|
if (payloadCategory) return payloadCategory;
|
||||||
|
const numericStatus = Number(status || 0);
|
||||||
|
if (numericStatus === 408) return "timeout";
|
||||||
|
if (numericStatus === 401 || numericStatus === 403) return "permission";
|
||||||
|
if (numericStatus === 413) return "payload-too-large";
|
||||||
|
if (numericStatus === 429) return "rate-limit";
|
||||||
|
if (numericStatus >= 500) return "server";
|
||||||
|
if (numericStatus >= 400) return "validation";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function classifyAuthorityProbeError(error = null) {
|
||||||
|
const category = String(error?.category || error?.errorCategory || "").trim();
|
||||||
|
if (category) return category;
|
||||||
|
if (String(error?.name || "") === "AbortError") return "timeout";
|
||||||
|
return error ? "network" : "";
|
||||||
|
}
|
||||||
|
|
||||||
function buildAuthorityPermissionEvaluateRequests(settings = {}, readiness = {}, options = {}) {
|
function buildAuthorityPermissionEvaluateRequests(settings = {}, readiness = {}, options = {}) {
|
||||||
const requests = [];
|
const requests = [];
|
||||||
const sqlTarget = String(options.sqlTarget || settings.sqlTarget || "default");
|
const sqlTarget = String(options.sqlTarget || settings.sqlTarget || "default");
|
||||||
@@ -198,6 +218,8 @@ async function verifyAuthorityDataPlane(baseUrl, fetchImpl, headers, settings =
|
|||||||
reason: initStatus === 401 || initStatus === 403 ? "session-init-denied" : "session-init-failed",
|
reason: initStatus === 401 || initStatus === 403 ? "session-init-denied" : "session-init-failed",
|
||||||
lastError: readPayloadMessage(initPayload, `HTTP ${initStatus || "unknown"}`),
|
lastError: readPayloadMessage(initPayload, `HTTP ${initStatus || "unknown"}`),
|
||||||
status: initStatus,
|
status: initStatus,
|
||||||
|
errorCategory: classifyAuthorityProbeStatus(initStatus, initPayload),
|
||||||
|
errorDomain: "authority",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +253,8 @@ async function verifyAuthorityDataPlane(baseUrl, fetchImpl, headers, settings =
|
|||||||
reason: currentStatus === 401 || currentStatus === 403 ? "session-invalid" : "session-current-failed",
|
reason: currentStatus === 401 || currentStatus === 403 ? "session-invalid" : "session-current-failed",
|
||||||
lastError: readPayloadMessage(currentPayload, `HTTP ${currentStatus || "unknown"}`),
|
lastError: readPayloadMessage(currentPayload, `HTTP ${currentStatus || "unknown"}`),
|
||||||
status: currentStatus,
|
status: currentStatus,
|
||||||
|
errorCategory: classifyAuthorityProbeStatus(currentStatus, currentPayload),
|
||||||
|
errorDomain: "authority",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,6 +283,8 @@ async function verifyAuthorityDataPlane(baseUrl, fetchImpl, headers, settings =
|
|||||||
reason: permissionStatus === 401 || permissionStatus === 403 ? "permission-denied" : "permission-evaluate-failed",
|
reason: permissionStatus === 401 || permissionStatus === 403 ? "permission-denied" : "permission-evaluate-failed",
|
||||||
lastError: readPayloadMessage(permissionPayload, `HTTP ${permissionStatus || "unknown"}`),
|
lastError: readPayloadMessage(permissionPayload, `HTTP ${permissionStatus || "unknown"}`),
|
||||||
status: permissionStatus,
|
status: permissionStatus,
|
||||||
|
errorCategory: classifyAuthorityProbeStatus(permissionStatus, permissionPayload),
|
||||||
|
errorDomain: "authority",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,6 +434,8 @@ export function createDefaultAuthorityCapabilityState(overrides = {}) {
|
|||||||
missingFeatures: ["sql.query", "sql.mutation", "trivium.search", "jobs", "blob-or-private-files"],
|
missingFeatures: ["sql.query", "sql.mutation", "trivium.search", "jobs", "blob-or-private-files"],
|
||||||
reason: "not-probed",
|
reason: "not-probed",
|
||||||
lastError: "",
|
lastError: "",
|
||||||
|
errorCategory: "",
|
||||||
|
errorDomain: "",
|
||||||
endpoint: "",
|
endpoint: "",
|
||||||
status: 0,
|
status: 0,
|
||||||
latencyMs: 0,
|
latencyMs: 0,
|
||||||
@@ -459,6 +487,8 @@ export function normalizeAuthorityCapabilityState(input = {}, settings = {}) {
|
|||||||
missingFeatures,
|
missingFeatures,
|
||||||
reason: String(source.reason || (healthy ? "ok" : "not-ready")),
|
reason: String(source.reason || (healthy ? "ok" : "not-ready")),
|
||||||
lastError: String(source.lastError || ""),
|
lastError: String(source.lastError || ""),
|
||||||
|
errorCategory: String(source.errorCategory || ""),
|
||||||
|
errorDomain: String(source.errorDomain || ""),
|
||||||
endpoint: String(source.endpoint || ""),
|
endpoint: String(source.endpoint || ""),
|
||||||
status: clampInteger(source.status, 0, 0, 999),
|
status: clampInteger(source.status, 0, 0, 999),
|
||||||
latencyMs: Math.max(0, Number(source.latencyMs) || 0),
|
latencyMs: Math.max(0, Number(source.latencyMs) || 0),
|
||||||
@@ -547,6 +577,7 @@ export async function probeAuthorityCapabilities(options = {}) {
|
|||||||
|
|
||||||
let lastError = "";
|
let lastError = "";
|
||||||
let lastStatus = 0;
|
let lastStatus = 0;
|
||||||
|
let lastErrorCategory = "";
|
||||||
for (const endpoint of buildAuthorityProbeUrls(settings.baseUrl)) {
|
for (const endpoint of buildAuthorityProbeUrls(settings.baseUrl)) {
|
||||||
const startedAt = readNowMs();
|
const startedAt = readNowMs();
|
||||||
try {
|
try {
|
||||||
@@ -555,6 +586,7 @@ export async function probeAuthorityCapabilities(options = {}) {
|
|||||||
const status = Number(response?.status || 0);
|
const status = Number(response?.status || 0);
|
||||||
lastStatus = status;
|
lastStatus = status;
|
||||||
if (status === 404) continue;
|
if (status === 404) continue;
|
||||||
|
const errorPayload = response?.ok ? null : await readResponsePayload(response);
|
||||||
if (status === 401 || status === 403) {
|
if (status === 401 || status === 403) {
|
||||||
return normalizeAuthorityCapabilityState(
|
return normalizeAuthorityCapabilityState(
|
||||||
{
|
{
|
||||||
@@ -563,7 +595,9 @@ export async function probeAuthorityCapabilities(options = {}) {
|
|||||||
sessionReady: false,
|
sessionReady: false,
|
||||||
permissionReady: false,
|
permissionReady: false,
|
||||||
reason: "permission-denied",
|
reason: "permission-denied",
|
||||||
lastError: `HTTP ${status}`,
|
lastError: readPayloadMessage(errorPayload, `HTTP ${status}`),
|
||||||
|
errorCategory: classifyAuthorityProbeStatus(status, errorPayload),
|
||||||
|
errorDomain: "authority",
|
||||||
endpoint,
|
endpoint,
|
||||||
status,
|
status,
|
||||||
latencyMs: normalizeLatencyMs(startedAt, finishedAt),
|
latencyMs: normalizeLatencyMs(startedAt, finishedAt),
|
||||||
@@ -579,7 +613,9 @@ export async function probeAuthorityCapabilities(options = {}) {
|
|||||||
installed: status > 0,
|
installed: status > 0,
|
||||||
healthy: false,
|
healthy: false,
|
||||||
reason: "http-error",
|
reason: "http-error",
|
||||||
lastError: `HTTP ${status || "unknown"}`,
|
lastError: readPayloadMessage(errorPayload, `HTTP ${status || "unknown"}`),
|
||||||
|
errorCategory: classifyAuthorityProbeStatus(status, errorPayload),
|
||||||
|
errorDomain: "authority",
|
||||||
endpoint,
|
endpoint,
|
||||||
status,
|
status,
|
||||||
latencyMs: normalizeLatencyMs(startedAt, finishedAt),
|
latencyMs: normalizeLatencyMs(startedAt, finishedAt),
|
||||||
@@ -605,12 +641,16 @@ export async function probeAuthorityCapabilities(options = {}) {
|
|||||||
let reason = missingFeatures.length ? "missing-required-features" : "ok";
|
let reason = missingFeatures.length ? "missing-required-features" : "ok";
|
||||||
let dataPlaneLastError = "";
|
let dataPlaneLastError = "";
|
||||||
let dataPlaneStatus = status;
|
let dataPlaneStatus = status;
|
||||||
|
let dataPlaneErrorCategory = "";
|
||||||
|
let dataPlaneErrorDomain = "";
|
||||||
if (healthy) {
|
if (healthy) {
|
||||||
const verified = await verifyAuthorityDataPlane(settings.baseUrl, fetchImpl, headers, settings, readiness, options);
|
const verified = await verifyAuthorityDataPlane(settings.baseUrl, fetchImpl, headers, settings, readiness, options);
|
||||||
sessionReady = verified.sessionReady;
|
sessionReady = verified.sessionReady;
|
||||||
permissionReady = verified.permissionReady;
|
permissionReady = verified.permissionReady;
|
||||||
dataPlaneStatus = Number(verified.status || status || 0);
|
dataPlaneStatus = Number(verified.status || status || 0);
|
||||||
dataPlaneLastError = String(verified.lastError || "");
|
dataPlaneLastError = String(verified.lastError || "");
|
||||||
|
dataPlaneErrorCategory = String(verified.errorCategory || "");
|
||||||
|
dataPlaneErrorDomain = String(verified.errorDomain || "");
|
||||||
if (verified.reason && verified.reason !== "ok") {
|
if (verified.reason && verified.reason !== "ok") {
|
||||||
reason = verified.reason;
|
reason = verified.reason;
|
||||||
}
|
}
|
||||||
@@ -627,6 +667,8 @@ export async function probeAuthorityCapabilities(options = {}) {
|
|||||||
missingFeatures,
|
missingFeatures,
|
||||||
reason,
|
reason,
|
||||||
lastError: dataPlaneLastError,
|
lastError: dataPlaneLastError,
|
||||||
|
errorCategory: dataPlaneErrorCategory,
|
||||||
|
errorDomain: dataPlaneErrorDomain,
|
||||||
endpoint,
|
endpoint,
|
||||||
status: dataPlaneStatus,
|
status: dataPlaneStatus,
|
||||||
latencyMs: normalizeLatencyMs(startedAt, finishedAt),
|
latencyMs: normalizeLatencyMs(startedAt, finishedAt),
|
||||||
@@ -637,6 +679,8 @@ export async function probeAuthorityCapabilities(options = {}) {
|
|||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
lastError = error?.message || String(error);
|
lastError = error?.message || String(error);
|
||||||
|
lastStatus = Number(error?.status || lastStatus || 0);
|
||||||
|
lastErrorCategory = classifyAuthorityProbeError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,6 +690,8 @@ export async function probeAuthorityCapabilities(options = {}) {
|
|||||||
healthy: false,
|
healthy: false,
|
||||||
reason: lastStatus === 404 ? "not-installed" : "probe-failed",
|
reason: lastStatus === 404 ? "not-installed" : "probe-failed",
|
||||||
lastError,
|
lastError,
|
||||||
|
errorCategory: lastErrorCategory || classifyAuthorityProbeStatus(lastStatus),
|
||||||
|
errorDomain: lastErrorCategory || lastStatus ? "authority" : "",
|
||||||
status: lastStatus,
|
status: lastStatus,
|
||||||
lastProbeAt: nowMs,
|
lastProbeAt: nowMs,
|
||||||
updatedAt: new Date(nowMs).toISOString(),
|
updatedAt: new Date(nowMs).toISOString(),
|
||||||
|
|||||||
@@ -176,6 +176,78 @@ const relativeUnavailable = await probeAuthorityCapabilities({
|
|||||||
assert.equal(relativeUnavailable.reason, "relative-url-unavailable");
|
assert.equal(relativeUnavailable.reason, "relative-url-unavailable");
|
||||||
assert.equal(relativeUnavailable.serverPrimaryReady, false);
|
assert.equal(relativeUnavailable.serverPrimaryReady, false);
|
||||||
|
|
||||||
|
const permissionDeniedState = await probeAuthorityCapabilities({
|
||||||
|
settings: defaultSettings,
|
||||||
|
allowRelativeUrl: true,
|
||||||
|
nowMs: 3100,
|
||||||
|
fetchImpl: async () => ({
|
||||||
|
ok: false,
|
||||||
|
status: 403,
|
||||||
|
async json() {
|
||||||
|
return { error: "permission denied" };
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
assert.equal(permissionDeniedState.reason, "permission-denied");
|
||||||
|
assert.equal(permissionDeniedState.errorCategory, "permission");
|
||||||
|
assert.equal(permissionDeniedState.errorDomain, "authority");
|
||||||
|
|
||||||
|
const rateLimitedState = await probeAuthorityCapabilities({
|
||||||
|
settings: defaultSettings,
|
||||||
|
allowRelativeUrl: true,
|
||||||
|
nowMs: 3200,
|
||||||
|
fetchImpl: async () => ({
|
||||||
|
ok: false,
|
||||||
|
status: 429,
|
||||||
|
async json() {
|
||||||
|
return { error: "slow down" };
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
assert.equal(rateLimitedState.reason, "http-error");
|
||||||
|
assert.equal(rateLimitedState.errorCategory, "rate-limit");
|
||||||
|
assert.equal(rateLimitedState.errorDomain, "authority");
|
||||||
|
|
||||||
|
const serverErrorState = await probeAuthorityCapabilities({
|
||||||
|
settings: defaultSettings,
|
||||||
|
allowRelativeUrl: true,
|
||||||
|
nowMs: 3300,
|
||||||
|
fetchImpl: async () => ({
|
||||||
|
ok: false,
|
||||||
|
status: 503,
|
||||||
|
async json() {
|
||||||
|
return { category: "backpressure", code: "job_queue_full" };
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
assert.equal(serverErrorState.reason, "http-error");
|
||||||
|
assert.equal(serverErrorState.errorCategory, "backpressure");
|
||||||
|
assert.equal(serverErrorState.errorDomain, "authority");
|
||||||
|
|
||||||
|
const networkFailedState = await probeAuthorityCapabilities({
|
||||||
|
settings: defaultSettings,
|
||||||
|
allowRelativeUrl: true,
|
||||||
|
nowMs: 3400,
|
||||||
|
fetchImpl: async () => {
|
||||||
|
throw new Error("fetch failed");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assert.equal(networkFailedState.reason, "probe-failed");
|
||||||
|
assert.equal(networkFailedState.errorCategory, "network");
|
||||||
|
assert.equal(networkFailedState.errorDomain, "authority");
|
||||||
|
|
||||||
|
const timeoutState = await probeAuthorityCapabilities({
|
||||||
|
settings: defaultSettings,
|
||||||
|
allowRelativeUrl: true,
|
||||||
|
nowMs: 3500,
|
||||||
|
fetchImpl: async () => {
|
||||||
|
throw Object.assign(new Error("aborted"), { name: "AbortError" });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
assert.equal(timeoutState.reason, "probe-failed");
|
||||||
|
assert.equal(timeoutState.errorCategory, "timeout");
|
||||||
|
assert.equal(timeoutState.errorDomain, "authority");
|
||||||
|
|
||||||
// Regression: Authority capability normalization records explicit supported job types from probe payloads.
|
// Regression: Authority capability normalization records explicit supported job types from probe payloads.
|
||||||
// When a probe payload provides jobs.supportedTypes, normalizeAuthorityCapabilityState should surface
|
// When a probe payload provides jobs.supportedTypes, normalizeAuthorityCapabilityState should surface
|
||||||
// them as supportedJobTypes and set supportedJobTypesKnown = true.
|
// them as supportedJobTypes and set supportedJobTypesKnown = true.
|
||||||
|
|||||||
Reference in New Issue
Block a user