fix: switch monitor root via sentinel yaml

This commit is contained in:
Codex
2026-06-28 03:28:10 +00:00
parent f371008af9
commit e4b9446ca9
8 changed files with 173 additions and 25 deletions
+6
View File
@@ -653,6 +653,12 @@ lanes:
observability:
prometheusOperator: false
webProbe:
monitorRoot:
enabled: true
sentinelId: workbench-fake-echo-session-invariance-10x
publicBaseUrl: https://monitor.pikapython.com
routePrefix: /
caddyManagedBlockOwner: hwlab-web-probe-sentinel-active-root
sentinels:
- id: workbench-dsflash-go-tool-call-10x
enabled: true
@@ -159,7 +159,7 @@ createApp({
function currentHref(item) {
if (!item || item.id === bootstrap.sentinelId) return bootstrap.basePath || "/";
if (item.id === "workbench-dsflash-go-tool-call-10x") return "/";
if (item.monitorRoot === true) return "/";
return `/sentinels/${encodeURIComponent(item.id)}/`;
}
+55 -4
View File
@@ -164,6 +164,14 @@ export interface HwlabRuntimeWebProbeSentinelRegistryItemSpec {
readonly configRef: string;
}
export interface HwlabRuntimeWebProbeMonitorRootSpec {
readonly enabled: boolean;
readonly sentinelId: string;
readonly publicBaseUrl: string;
readonly routePrefix: "/";
readonly caddyManagedBlockOwner: string;
}
export interface HwlabRuntimeWebProbeAlertThresholdsSpec {
readonly sameOriginApiSlowMs: number;
readonly partialApiSlowMs: number;
@@ -206,6 +214,7 @@ export interface HwlabRuntimeObservabilitySpec {
export interface HwlabRuntimeObservabilityWebProbeSpec {
readonly sentinel?: HwlabRuntimeWebProbeSentinelSpec;
readonly sentinels?: readonly HwlabRuntimeWebProbeSentinelRegistryItemSpec[];
readonly monitorRoot?: HwlabRuntimeWebProbeMonitorRootSpec;
}
export interface HwlabRuntimeObservabilityMetricsEndpointSpec {
@@ -1141,14 +1150,56 @@ function observabilityConfig(value: unknown, path: string): HwlabRuntimeObservab
function observabilityWebProbeConfig(value: unknown, path: string): HwlabRuntimeObservabilityWebProbeSpec | undefined {
if (value === undefined) return undefined;
const raw = asRecord(value, path);
const allowed = new Set(["sentinel", "sentinels"]);
const allowed = new Set(["sentinel", "sentinels", "monitorRoot"]);
for (const key of Object.keys(raw)) {
if (!allowed.has(key)) throw new Error(`${path}.${key} is not allowed; observability.webProbe currently only owns sentinel/sentinels`);
if (!allowed.has(key)) throw new Error(`${path}.${key} is not allowed; observability.webProbe currently only owns sentinel/sentinels/monitorRoot`);
}
if (raw.sentinel !== undefined && raw.sentinels !== undefined) throw new Error(`${path} may declare sentinel or sentinels, not both`);
const sentinel = raw.sentinel === undefined ? undefined : webProbeSentinelConfig(raw.sentinel, `${path}.sentinel`);
const sentinels = raw.sentinels === undefined ? undefined : webProbeSentinelRegistryConfig(raw.sentinels, `${path}.sentinels`);
const monitorRoot = raw.monitorRoot === undefined ? undefined : webProbeMonitorRootConfig(raw.monitorRoot, `${path}.monitorRoot`);
if (monitorRoot !== undefined) {
if (sentinels !== undefined && !sentinels.some((item) => item.id === monitorRoot.sentinelId)) {
throw new Error(`${path}.monitorRoot.sentinelId must reference one entry from ${path}.sentinels`);
}
if (sentinel !== undefined && monitorRoot.sentinelId !== "workbench-dsflash-go-tool-call-10x") {
throw new Error(`${path}.monitorRoot.sentinelId must be workbench-dsflash-go-tool-call-10x for legacy sentinel config`);
}
}
return {
...(raw.sentinel === undefined ? {} : { sentinel: webProbeSentinelConfig(raw.sentinel, `${path}.sentinel`) }),
...(raw.sentinels === undefined ? {} : { sentinels: webProbeSentinelRegistryConfig(raw.sentinels, `${path}.sentinels`) }),
...(sentinel === undefined ? {} : { sentinel }),
...(sentinels === undefined ? {} : { sentinels }),
...(monitorRoot === undefined ? {} : { monitorRoot }),
};
}
function webProbeMonitorRootConfig(value: unknown, path: string): HwlabRuntimeWebProbeMonitorRootSpec {
const raw = asRecord(value, path);
const allowed = new Set(["enabled", "sentinelId", "publicBaseUrl", "routePrefix", "caddyManagedBlockOwner"]);
for (const key of Object.keys(raw)) {
if (!allowed.has(key)) throw new Error(`${path}.${key} is not allowed; monitorRoot may only contain enabled/sentinelId/publicBaseUrl/routePrefix/caddyManagedBlockOwner`);
}
const sentinelId = stringField(raw, "sentinelId", path);
if (!/^[a-z0-9][a-z0-9-]{1,80}$/u.test(sentinelId)) throw new Error(`${path}.sentinelId must be a stable lowercase sentinel id`);
const publicBaseUrl = stringField(raw, "publicBaseUrl", path).replace(/\/+$/u, "");
try {
const parsed = new URL(publicBaseUrl);
if (parsed.protocol !== "https:") throw new Error("must use https");
if (parsed.pathname !== "/" || parsed.search.length > 0 || parsed.hash.length > 0) throw new Error("must point to the public origin root");
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`${path}.publicBaseUrl must be an https origin root URL: ${message}`);
}
const routePrefix = stringField(raw, "routePrefix", path);
if (routePrefix !== "/") throw new Error(`${path}.routePrefix must be / for the monitor root switch`);
const caddyManagedBlockOwner = stringField(raw, "caddyManagedBlockOwner", path);
if (!/^[a-z0-9][a-z0-9-]{1,100}$/u.test(caddyManagedBlockOwner)) throw new Error(`${path}.caddyManagedBlockOwner must be a stable lowercase owner id`);
return {
enabled: booleanField(raw, "enabled", path),
sentinelId,
publicBaseUrl,
routePrefix,
caddyManagedBlockOwner,
};
}
+22 -6
View File
@@ -12,7 +12,7 @@ import { repoRoot, rootPath } from "./config";
import { runCommand, type CommandResult } from "./command";
import { startJob } from "./jobs";
import { webProbeSentinelConfigPlan, withWebProbeSentinelConfigRendered } from "./hwlab-node-web-sentinel-config";
import { requireSentinelIdForRegistry, resolveWebProbeSentinel } from "./hwlab-node-web-sentinel-resolver";
import { effectiveWebProbeSentinelPublicExposure, requireSentinelIdForRegistry, resolveWebProbeSentinel } from "./hwlab-node-web-sentinel-resolver";
import type { HwlabRuntimeLaneSpec } from "./hwlab-node-lanes";
import type { RenderedCliResult } from "./output";
import { runWebProbeRemoteArtifactJob } from "./web-probe-remote-artifact";
@@ -310,7 +310,8 @@ function loadSentinelCicdState(spec: HwlabRuntimeLaneSpec, sentinelId: string |
const runtime = recordTarget(readConfigRefTarget(sentinel.configRefs.runtime), sentinel.configRefs.runtime);
const cicd = recordTarget(readConfigRefTarget(sentinel.configRefs.cicd), sentinel.configRefs.cicd);
const scenarios = readConfigRefTarget(sentinel.configRefs.scenarios);
const publicExposure = recordTarget(readConfigRefTarget(sentinel.configRefs.publicExposure), sentinel.configRefs.publicExposure);
const rawPublicExposure = recordTarget(readConfigRefTarget(sentinel.configRefs.publicExposure), sentinel.configRefs.publicExposure);
const publicExposure = effectiveWebProbeSentinelPublicExposure(spec, sentinel.id, rawPublicExposure);
const secrets = recordTarget(readConfigRefTarget(sentinel.configRefs.secrets), sentinel.configRefs.secrets);
const controlPlaneRef = stringField(cicd, "controlPlaneConfigRef");
const controlPlaneTarget = recordTarget(readConfigRefTarget(controlPlaneRef), controlPlaneRef);
@@ -3584,6 +3585,9 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
const responseHeaderTimeoutSeconds = numberAt(state.publicExposure, "caddy.responseHeaderTimeoutSeconds");
const remotePort = numberAt(state.publicExposure, "frpc.httpProxy.remotePort");
const routePrefix = normalizeRoutePrefix(stringAtNullable(state.publicExposure, "routePrefix"));
const rootOrder = stringAtNullable(state.publicExposure, "caddy.rootOrder") ?? "normal";
const monitorRoot = record(state.publicExposure.monitorRoot);
const cleanupOwner = monitorRoot.enabled === false ? stringAtNullable(monitorRoot, "caddyManagedBlockOwner") : null;
const proxyLines = [
`reverse_proxy 127.0.0.1:${remotePort} {`,
" transport http {",
@@ -3605,6 +3609,8 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
`config_path=${shellQuote(configPath)}`,
`service=${shellQuote(serviceName)}`,
`route_prefix=${shellQuote(routePrefix)}`,
`root_order=${shellQuote(rootOrder)}`,
`cleanup_owner=${shellQuote(cleanupOwner ?? "")}`,
`block_b64=${shellQuote(blockB64)}`,
"marker=\"unidesk managed $owner\"",
"tmp=$(mktemp -d)",
@@ -3613,17 +3619,21 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
"next=\"$tmp/Caddyfile\"",
"printf '%s' \"$block_b64\" | base64 -d >\"$block\"",
"if [ -f \"$config_path\" ]; then cp \"$config_path\" \"$next\"; else : >\"$next\"; fi",
"python3 - \"$next\" \"$block\" \"$marker\" \"$hostname\" \"$route_prefix\" <<'PY' >/tmp/web-probe-sentinel-caddy-python.out 2>/tmp/web-probe-sentinel-caddy-python.err",
"python3 - \"$next\" \"$block\" \"$marker\" \"$hostname\" \"$route_prefix\" \"$root_order\" \"$cleanup_owner\" <<'PY' >/tmp/web-probe-sentinel-caddy-python.out 2>/tmp/web-probe-sentinel-caddy-python.err",
"import pathlib, re, sys",
"config = pathlib.Path(sys.argv[1])",
"block = pathlib.Path(sys.argv[2]).read_text(encoding='utf-8')",
"marker = sys.argv[3]",
"hostname = sys.argv[4]",
"route_prefix = sys.argv[5]",
"root_order = sys.argv[6]",
"cleanup_owner = sys.argv[7]",
"text = config.read_text(encoding='utf-8') if config.exists() else ''",
"begin = f'# BEGIN {marker}'",
"end = f'# END {marker}'",
"pattern = re.compile(rf'(?ms)^[ \\t]*# BEGIN {re.escape(marker)}\\n.*?^[ \\t]*# END {re.escape(marker)}\\n*')",
"def managed_pattern(marker_text):",
" return re.compile(rf'(?ms)^[ \\t]*# BEGIN {re.escape(marker_text)}\\n.*?^[ \\t]*# END {re.escape(marker_text)}\\n*')",
"pattern = managed_pattern(marker)",
"def collect_nested_managed(segment):",
" preserved = []",
" lines = segment.splitlines()",
@@ -3647,6 +3657,10 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
"for match in pattern.finditer(text):",
" preserved_blocks.extend(collect_nested_managed(match.group(0)))",
"text = pattern.sub('', text)",
"if cleanup_owner:",
" cleanup_marker = f'unidesk managed {cleanup_owner}'",
" if cleanup_marker != marker:",
" text = managed_pattern(cleanup_marker).sub('', text)",
"def site_span(src, host):",
" match = re.search(rf'(?m)^([ \\t]*){re.escape(host)}[ \\t]*\\{{[ \\t]*\\n', src)",
" if not match:",
@@ -3696,7 +3710,9 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
" relative_open = open_end - start",
" close_rel = close_index - start",
" additions = ''.join(preserved_blocks) + managed",
" if route_prefix == '/':",
" if route_prefix == '/' and root_order == 'active':",
" replacement = site[:relative_open] + additions + site[relative_open:]",
" elif route_prefix == '/':",
" replacement = append_before_close(site, close_rel, additions)",
" else:",
" insert_at = fallback_insert_pos(site, relative_open, close_rel)",
@@ -3753,7 +3769,7 @@ function applySentinelCaddyBlock(state: SentinelCicdState, timeoutSeconds: numbe
].join("\n");
const result = runCommand(["trans", stringAt(state.publicExposure, "caddy.route"), "sh", "--", script], repoRoot, { timeoutMs: Math.min(timeoutSeconds, 60) * 1000 });
const parsed = parseJsonObject(result.stdout);
return { ok: result.exitCode === 0 && parsed?.ok === true, routePrefix, ...record(parsed), result: compactCommand(result), valuesRedacted: true };
return { ok: result.exitCode === 0 && parsed?.ok === true, routePrefix, rootOrder, ...record(parsed), result: compactCommand(result), valuesRedacted: true };
}
function readAnalysisSummaryFromWorkspace(state: SentinelCicdState, stateDir: string, timeoutSeconds: number): Record<string, unknown> {
+27 -5
View File
@@ -5,7 +5,7 @@ import { createHash } from "node:crypto";
import { existsSync, readFileSync } from "node:fs";
import { rootPath } from "./config";
import { HWLAB_WEB_PROBE_SENTINEL_CONFIG_REF_KEYS, type HwlabRuntimeLaneSpec, type HwlabRuntimeWebProbeSentinelConfigRefKey } from "./hwlab-node-lanes";
import { resolveWebProbeSentinel, webProbeSentinelRegistryRows, type WebProbeSentinelRegistryRow } from "./hwlab-node-web-sentinel-resolver";
import { effectiveWebProbeSentinelPublicExposure, resolveWebProbeSentinel, webProbeSentinelRegistryRows, type WebProbeSentinelRegistryRow } from "./hwlab-node-web-sentinel-resolver";
import type { RenderedCliResult } from "./output";
export type WebProbeSentinelConfigAction = "plan" | "status";
@@ -211,7 +211,9 @@ export function webProbeSentinelConfigPlan(spec: HwlabRuntimeLaneSpec, action: W
}
const selected = resolveWebProbeSentinel(spec, sentinelId);
const refs = HWLAB_WEB_PROBE_SENTINEL_CONFIG_REF_KEYS.map((key) => readSentinelConfigRef(key, selected.configRefs[key]));
const refs = HWLAB_WEB_PROBE_SENTINEL_CONFIG_REF_KEYS
.map((key) => readSentinelConfigRef(key, selected.configRefs[key]))
.map((ref) => effectiveConfigRefStatus(spec, selected.id, ref));
const conflicts = selected.enabled ? crossReferenceConflicts(spec, refs) : [];
const refBlocked = refs.some((ref) => !ref.present || !ref.targetPresent || ref.missingFields.length > 0 || ref.conflicts.length > 0 || ref.error !== null);
const ok = selected.enabled && !refBlocked && conflicts.length === 0;
@@ -232,6 +234,18 @@ export function webProbeSentinelConfigPlan(spec: HwlabRuntimeLaneSpec, action: W
};
}
function effectiveConfigRefStatus(spec: HwlabRuntimeLaneSpec, sentinelId: string, ref: InternalConfigRefStatus): InternalConfigRefStatus {
if (ref.key !== "publicExposure" || !isRecord(ref.target)) return ref;
const target = effectiveWebProbeSentinelPublicExposure(spec, sentinelId, ref.target);
return {
...ref,
targetKind: targetKindOf(target),
missingFields: missingFieldsForTarget(ref.key, target),
summary: summarizeTarget(ref.key, target),
target,
};
}
export function withWebProbeSentinelConfigRendered(value: WebProbeSentinelConfigPlan): RenderedCliResult {
return {
ok: value.ok,
@@ -416,7 +430,15 @@ function summarizeTarget(key: HwlabRuntimeWebProbeSentinelConfigRefKey, target:
if (key === "runtime") return `namespace=${textAt(target, "namespace")} service=${textAt(target, "serviceName")} image=${short(textAt(target, "imageRef"), 48)}`;
if (key === "promptSet") return `id=${textAt(target, "id")} provider=${textAt(target, "providerProfile")} prompts=${textAt(target, "promptCount")} markers=${arrayAt(target, "expectedMarkers").slice(0, 12).join(",") || "-"} source=${textAt(target, "promptSourceRef")}:${textAt(target, "promptSourceKey")}`;
if (key === "reportViews") return `default=${textAt(target, "defaultView")} views=${arrayAt(target, "views").length}`;
if (key === "publicExposure") return `enabled=${textAt(target, "enabled")} mode=${textAt(target, "mode")} url=${textAt(target, "publicBaseUrl")}`;
if (key === "publicExposure") {
const monitorRoot = textAt(target, "monitorRoot.enabled");
const rootOrder = textAt(target, "caddy.rootOrder");
const suffix = [
...(monitorRoot === "-" ? [] : [`monitorRoot=${monitorRoot}`]),
...(rootOrder === "-" ? [] : [`rootOrder=${rootOrder}`]),
].join(" ");
return `enabled=${textAt(target, "enabled")} mode=${textAt(target, "mode")} url=${textAt(target, "publicBaseUrl")}${suffix.length === 0 ? "" : ` ${suffix}`}`;
}
if (key === "cicd") return `gitops=${textAt(target, "gitopsPath")} image=${textAt(target, "image.repository")}:${textAt(target, "image.tagSource")} confirmWait=${textAt(target, "confirmWait.maxSeconds")} targetValidation=${textAt(target, "targetValidation.maxSeconds")}`;
if (key === "secrets") return `sources=${arrayAt(target, "sources").length} runtimeSecrets=${arrayAt(target, "runtimeSecrets").length}`;
return `keys=${Object.keys(target).length}`;
@@ -442,8 +464,8 @@ function renderWebProbeSentinelConfigPlan(value: WebProbeSentinelConfigPlan): st
...(value.sentinels.length === 0 ? [] : [
"",
sentinelTable(
["SENTINEL", "ENABLED", "CONFIG_REF"],
value.sentinels.map((item) => [item.id, item.enabled, short(item.configRef, 110)]),
["SENTINEL", "ENABLED", "ROOT", "CONFIG_REF"],
value.sentinels.map((item) => [item.id, item.enabled, item.monitorRoot ? "monitor-root" : "-", short(item.configRef, 110)]),
),
]),
...(value.refs.length === 0 ? [
@@ -61,10 +61,11 @@ export function renderWebProbeSentinelDashboardHtml(config: DashboardShellConfig
</html>`;
}
function sentinelRegistryRows(config: DashboardShellConfig): Array<{ readonly id: string; readonly enabled: boolean }> {
function sentinelRegistryRows(config: DashboardShellConfig): Array<{ readonly id: string; readonly enabled: boolean; readonly monitorRoot: boolean }> {
return Array.isArray(config.plan.sentinels) ? config.plan.sentinels.map((item) => ({
id: stringOrNull(item.id) ?? "",
enabled: item.enabled !== false,
monitorRoot: item.monitorRoot === true,
})).filter((item) => item.id.length > 0) : [];
}
@@ -18,18 +18,57 @@ export interface WebProbeSentinelRegistryRow {
readonly id: string;
readonly enabled: boolean;
readonly configRef: string;
readonly monitorRoot: boolean;
readonly publicBaseUrl?: string;
readonly routePrefix?: string;
}
export function webProbeSentinelRegistryRows(spec: HwlabRuntimeLaneSpec): readonly WebProbeSentinelRegistryRow[] {
const registry = spec.observability.webProbe?.sentinels;
if (registry !== undefined) return registry.map((item) => ({ id: item.id, enabled: item.enabled, configRef: item.configRef }));
if (registry !== undefined) return registry.map((item) => sentinelRegistryRow(spec, item.id, item.enabled, item.configRef));
const legacy = spec.observability.webProbe?.sentinel;
if (legacy === undefined) return [];
return [{
id: "workbench-dsflash-go-tool-call-10x",
enabled: legacy.enabled,
configRef: `config/hwlab-node-lanes.yaml#lanes.${spec.lane}.targets.${spec.nodeId}.observability.webProbe.sentinel`,
}];
return [sentinelRegistryRow(
spec,
"workbench-dsflash-go-tool-call-10x",
legacy.enabled,
`config/hwlab-node-lanes.yaml#lanes.${spec.lane}.targets.${spec.nodeId}.observability.webProbe.sentinel`,
)];
}
export function effectiveWebProbeSentinelPublicExposure(spec: HwlabRuntimeLaneSpec, sentinelId: string, publicExposure: Record<string, unknown>): Record<string, unknown> {
const monitorRoot = spec.observability.webProbe?.monitorRoot;
if (monitorRoot === undefined || monitorRoot.sentinelId !== sentinelId) return publicExposure;
if (!monitorRoot.enabled) {
return {
...publicExposure,
monitorRoot: {
enabled: false,
sentinelId,
caddyManagedBlockOwner: monitorRoot.caddyManagedBlockOwner,
valuesRedacted: true,
},
};
}
const caddy = isRecord(publicExposure.caddy) ? publicExposure.caddy : {};
return {
...publicExposure,
publicBaseUrl: monitorRoot.publicBaseUrl,
routePrefix: monitorRoot.routePrefix,
caddy: {
...caddy,
managedBlockOwner: monitorRoot.caddyManagedBlockOwner,
rootOrder: "active",
},
monitorRoot: {
enabled: true,
sentinelId,
publicBaseUrl: monitorRoot.publicBaseUrl,
routePrefix: monitorRoot.routePrefix,
caddyManagedBlockOwner: monitorRoot.caddyManagedBlockOwner,
valuesRedacted: true,
},
};
}
export function resolveWebProbeSentinel(spec: HwlabRuntimeLaneSpec, sentinelId: string | null | undefined): ResolvedWebProbeSentinel {
@@ -87,6 +126,18 @@ function resolveRegistrySentinel(spec: HwlabRuntimeLaneSpec, registry: readonly
};
}
function sentinelRegistryRow(spec: HwlabRuntimeLaneSpec, id: string, enabled: boolean, configRef: string): WebProbeSentinelRegistryRow {
const monitorRoot = spec.observability.webProbe?.monitorRoot;
const isMonitorRoot = monitorRoot?.enabled === true && monitorRoot.sentinelId === id;
return {
id,
enabled,
configRef,
monitorRoot: isMonitorRoot,
...(isMonitorRoot ? { publicBaseUrl: monitorRoot.publicBaseUrl, routePrefix: monitorRoot.routePrefix } : {}),
};
}
function normalizeSentinelConfigRefs(target: Record<string, unknown>, ref: string): Record<HwlabRuntimeWebProbeSentinelConfigRefKey, string> {
const rawRefs = recordAt(target, "configRefs");
const normalized: Record<string, string> = {};
@@ -13,7 +13,7 @@ import { rootPath } from "./config";
import { renderWebProbeSentinelDashboardHtml, webProbeSentinelDashboardAssetResponse } from "./hwlab-node-web-sentinel-dashboard-assets";
import { webProbeSentinelConfigPlan, type WebProbeSentinelConfigPlan } from "./hwlab-node-web-sentinel-config";
import type { HwlabRuntimeLaneSpec } from "./hwlab-node-lanes";
import { resolveWebProbeSentinel, readConfigRefTarget as readSentinelConfigRefTarget } from "./hwlab-node-web-sentinel-resolver";
import { effectiveWebProbeSentinelPublicExposure, resolveWebProbeSentinel, readConfigRefTarget as readSentinelConfigRefTarget } from "./hwlab-node-web-sentinel-resolver";
const DASHBOARD_CONTRACT_VERSION = "draft-2026-06-27-p11-monitor-web-observability-dashboard";
const DASHBOARD_MAX_TEXT_BYTES = 16_000;
@@ -93,7 +93,8 @@ export function loadWebProbeSentinelServiceConfig(spec: HwlabRuntimeLaneSpec, op
const runtime = recordTarget(readSentinelConfigRefTarget(sentinel.configRefs.runtime));
const scenarios = scenarioArrayTarget(readSentinelConfigRefTarget(sentinel.configRefs.scenarios));
const reportViews = recordTarget(readSentinelConfigRefTarget(sentinel.configRefs.reportViews));
const publicExposure = recordTarget(readSentinelConfigRefTarget(sentinel.configRefs.publicExposure));
const rawPublicExposure = recordTarget(readSentinelConfigRefTarget(sentinel.configRefs.publicExposure));
const publicExposure = effectiveWebProbeSentinelPublicExposure(spec, sentinel.id, rawPublicExposure);
const cicd = recordTarget(readSentinelConfigRefTarget(sentinel.configRefs.cicd));
const stateRoot = options.stateRootOverride ?? stringAt(runtime, "stateRoot");
const yamlSqlitePath = stringAt(runtime, "sqlite.path");