Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 153 additions & 3 deletions apps/dashboard/src/data/workbench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type WorkbenchSectionKey =
| "liquidityPositions"
| "swaps"
| "explorerRecords"
| "realValuePilot"
| "rootfields"
| "agents"
| "models"
Expand Down Expand Up @@ -75,6 +76,7 @@ export interface ControlPlaneProbe {
error?: string;
health?: unknown;
state?: unknown;
pilotStatus?: unknown;
}

export interface WorkbenchNodeStatus {
Expand Down Expand Up @@ -112,6 +114,7 @@ export interface WorkbenchSnapshot {
devnetState: unknown | null;
devnetDashboardState: unknown | null;
bridgeTestDeposit: unknown | null;
controlPlanePilotStatus: unknown | null;
controlPlaneHealth: unknown | null;
controlPlaneState: unknown | null;
};
Expand Down Expand Up @@ -240,6 +243,14 @@ export const WORKBENCH_SECTIONS: WorkbenchSectionDefinition[] = [
missingCommand: "npm run flowchain:product-e2e",
missingService: "FlowChain explorer API /explorer",
},
{
key: "realValuePilot",
label: "Real-Value Pilot",
detail: "Capped owner-testing lifecycle for Base deposit observation, local credit, replay/retry status, withdrawal intent, release evidence, caps, pause, and emergency state.",
expectedEndpoint: "GET /pilot/status + POST /rpc pilot_status",
missingCommand: "npm run control-plane:serve",
missingService: "FlowChain real-value pilot control-plane /pilot/status",
},
{
key: "rootfields",
label: "Rootfields",
Expand Down Expand Up @@ -483,7 +494,7 @@ function stringArray(value: unknown): string[] {

function statusFrom(value: unknown, fallback: DashboardStatus = "observed"): DashboardStatus {
const normalized = text(value, fallback).toLowerCase();
if (normalized === "applied" || normalized === "success" || normalized === "active") {
if (normalized === "applied" || normalized === "success" || normalized === "active" || normalized === "live") {
return "verified";
}
if (normalized === "finalized") {
Expand All @@ -492,9 +503,12 @@ function statusFrom(value: unknown, fallback: DashboardStatus = "observed"): Das
if (normalized === "failed" || normalized === "invalid" || normalized === "reverted") {
return "failed";
}
if (normalized === "pending" || normalized === "local-placeholder") {
if (normalized === "pending" || normalized === "local-placeholder" || normalized === "degraded") {
return "pending";
}
if (normalized === "error") {
return "failed";
}
if (normalized === "stale" || normalized === "not-detected") {
return "stale";
}
Expand Down Expand Up @@ -684,11 +698,18 @@ async function fetchOptionalJson(path: string): Promise<{ value: unknown | null;
async function probeControlPlane(): Promise<ControlPlaneProbe> {
const url = getControlPlaneUrl();
const checkedAt = new Date().toISOString();
const defaultEndpoints = ["GET /health", "GET /state"];
const defaultEndpoints = ["GET /health", "GET /state", "GET /pilot/status"];

try {
const health = await fetchJsonWithTimeout(`${url}/health`, CONTROL_PLANE_TIMEOUT_MS);
let state: unknown | undefined;
let pilotStatus: unknown | undefined;

try {
pilotStatus = await fetchJsonWithTimeout(`${url}/pilot/status`, CONTROL_PLANE_TIMEOUT_MS);
} catch {
pilotStatus = undefined;
}

try {
state = await fetchJsonWithTimeout(`${url}/state`, CONTROL_PLANE_TIMEOUT_MS);
Expand All @@ -699,6 +720,7 @@ async function probeControlPlane(): Promise<ControlPlaneProbe> {
checkedAt,
endpoints: uniqueEndpoints(defaultEndpoints, collectEndpointHints(health)),
health,
pilotStatus,
error: `Health endpoint responded, but state endpoint was not loaded: ${
error instanceof Error ? error.message : "unknown state error"
}`,
Expand All @@ -712,6 +734,7 @@ async function probeControlPlane(): Promise<ControlPlaneProbe> {
endpoints: uniqueEndpoints(defaultEndpoints, collectEndpointHints(health), collectEndpointHints(state)),
health,
state,
pilotStatus,
};
} catch (error) {
return {
Expand Down Expand Up @@ -1079,6 +1102,131 @@ function buildBridgeRecords(
});
}

function commandFromStep(step: unknown): string {
return isRecord(step) ? text(step.command, "npm run flowchain:real-value-pilot:e2e") : "npm run flowchain:real-value-pilot:e2e";
}

function buildPilotRecords(controlPlane: ControlPlaneProbe): WorkbenchRecord[] {
const pilot = isRecord(controlPlane.pilotStatus) ? controlPlane.pilotStatus : null;
const records: WorkbenchRecord[] = [];

if (!pilot) {
records.push(
makeLocalRecord(
"devnet",
controlPlane.url,
{
id: "real-value-pilot-api",
kind: "Pilot status",
title: "Pilot API not detected",
summary: "The real-value pilot control-plane status is unavailable; the dashboard is waiting for the local API endpoint.",
status: controlPlane.status === "available" ? "pending" : "offline",
facts: [
{ label: "state", value: controlPlane.status === "available" ? "degraded" : "offline" },
{ label: "scope", value: "capped owner testing" },
{ label: "public readiness", value: "false" },
{ label: "next command", value: "npm run control-plane:serve" },
],
raw: controlPlane,
},
controlPlane.checkedAt,
),
);
return records;
}

const nextStep = isRecord(pilot.nextOperatorStep) ? pilot.nextOperatorStep : {};
const lifecycle = collectionFrom(pilot, ["lifecycle"]);
const capStatus = isRecord(pilot.capStatus) ? pilot.capStatus : null;
const pauseStatus = isRecord(pilot.pauseStatus) ? pilot.pauseStatus : null;
const retryStatus = isRecord(pilot.retryStatus) ? pilot.retryStatus : null;
const emergencyStatus = isRecord(pilot.emergencyStatus) ? pilot.emergencyStatus : null;
const state = text(pilot.state, "degraded");

records.push(
makeLocalRecord(
"devnet",
controlPlane.url,
{
id: text(pilot.pilotId, "real-value-pilot-status"),
kind: "Pilot status",
title: `Pilot ${state}`,
summary: text(pilot.stateReason, "Capped owner-testing pilot status is loaded from the local control-plane API."),
status: statusFrom(state, "pending"),
facts: [
{ label: "state", value: state },
{ label: "base chain", value: text(pilot.baseChainId, "8453") },
{ label: "scope", value: text(pilot.label, "FlowChain capped owner real-value pilot") },
{ label: "public readiness", value: text(pilot.broadPublicReadiness, "false") },
{ label: "browser stores secrets", value: text(pilot.browserStoresSecrets, "false") },
{ label: "next command", value: commandFromStep(nextStep) },
],
raw: pilot,
},
controlPlane.checkedAt,
),
);

lifecycle.forEach((step, index) => {
records.push(
makeLocalRecord(
"devnet",
controlPlane.url,
{
id: text(step.phase, `pilot-step:${index + 1}`),
kind: "Pilot lifecycle",
title: text(step.title, "Pilot lifecycle step"),
summary: text(step.summary, "Pilot lifecycle state exported by the control-plane."),
status: statusFrom(step.state, "pending"),
facts: [
{ label: "state", value: text(step.state) },
{ label: "phase", value: text(step.phase) },
{ label: "next command", value: text(step.nextOperatorCommand, commandFromStep(nextStep)) },
{ label: "evidence", value: stringArray(step.evidenceIds).join(", ") || "not recorded" },
],
raw: step,
},
controlPlane.checkedAt,
),
);
});

[
{ id: "pilot-cap-status", title: "Cap status", raw: capStatus },
{ id: "pilot-pause-status", title: "Pause status", raw: pauseStatus },
{ id: "pilot-retry-status", title: "Retry status", raw: retryStatus },
{ id: "pilot-emergency-status", title: "Emergency status", raw: emergencyStatus },
].forEach((item) => {
const raw = item.raw;
if (!raw) {
return;
}
records.push(
makeLocalRecord(
"devnet",
controlPlane.url,
{
id: item.id,
kind: "Pilot guardrail",
title: item.title,
summary: `Pilot ${item.title.toLowerCase()} is ${text(raw.state, "degraded")}.`,
status: statusFrom(raw.state, "pending"),
facts: [
{ label: "state", value: text(raw.state) },
{ label: "status", value: text(raw.status ?? raw.withinCap ?? raw.active) },
{ label: "next command", value: text(raw.nextOperatorCommand, commandFromStep(nextStep)) },
{ label: "production ready", value: text(raw.productionReady, "false") },
],
raw,
},
controlPlane.checkedAt,
),
);
});

return records;
}

function buildBlockRecords(data: DashboardData, devnetState: unknown): WorkbenchRecord[] {
const blocks = collectionFrom(devnetState, ["blocks"]);

Expand Down Expand Up @@ -1967,6 +2115,7 @@ export function buildWorkbenchSnapshot(
bridgeDeposits: buildBridgeRecords(activeDevnetState, "deposits", bridgeTestDeposit),
bridgeCredits: buildBridgeRecords(activeDevnetState, "credits", bridgeTestDeposit),
bridgeWithdrawals: buildBridgeRecords(activeDevnetState, "withdrawals", bridgeTestDeposit),
realValuePilot: buildPilotRecords(controlPlane),
provenance: [],
hardwareSignals: buildHardwareSignalRecords(data, activeDevnetState),
rawJson: [],
Expand Down Expand Up @@ -2002,6 +2151,7 @@ export function buildWorkbenchSnapshot(
devnetState: options.devnetState ?? null,
devnetDashboardState: options.devnetDashboardState ?? null,
bridgeTestDeposit,
controlPlanePilotStatus: controlPlane.pilotStatus ?? null,
controlPlaneHealth: controlPlane.health ?? null,
controlPlaneState: controlPlane.state ?? null,
},
Expand Down
30 changes: 30 additions & 0 deletions apps/dashboard/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,35 @@ dd {
line-height: 1.42;
}

.pilot-status-panel article {
display: grid;
gap: 14px;
min-width: 0;
padding: 14px;
border: 1px solid #b8c7c0;
border-radius: 8px;
background: #f4f8f6;
}

.pilot-status-body {
display: grid;
grid-template-columns: minmax(0, 0.92fr) minmax(320px, 1.08fr);
gap: 14px;
align-items: start;
}

.pilot-status-body h3 {
margin: 4px 0 8px;
font-size: 1.35rem;
text-transform: capitalize;
}

.pilot-status-body p {
margin: 0;
color: #3f483f;
line-height: 1.48;
}

.product-surface-grid {
display: grid;
grid-template-columns: 1.2fr 1fr 1.2fr;
Expand Down Expand Up @@ -1390,6 +1419,7 @@ code {
.canary-operator-strip,
.workbench-boundary-strip,
.product-surface-grid,
.pilot-status-body,
.local-action-grid,
.workbench-command-center,
.workbench-record-grid {
Expand Down
54 changes: 54 additions & 0 deletions apps/dashboard/src/test/dashboardData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ describe("dashboard fixture", () => {
expect(workbench.sections.bridgeDeposits.length).toBeGreaterThan(0);
expect(workbench.sections.bridgeCredits).toHaveLength(0);
expect(workbench.sections.bridgeWithdrawals).toHaveLength(0);
expect(workbench.sections.realValuePilot.length).toBeGreaterThan(0);
expect(workbench.sections.realValuePilot[0].facts.find((fact) => fact.label === "scope")?.value).toBe("capped owner testing");
expect(workbench.sections.explorerRecords.length).toBeGreaterThan(0);
expect(workbench.node.status).toBe("offline");
expect(workbench.actions).toEqual([]);
Expand All @@ -162,6 +164,34 @@ describe("dashboard fixture", () => {
endpoints: ["GET /health", "GET /state"],
health: { status: "ok" },
state: devnetState,
pilotStatus: {
schema: "flowmemory.control_plane.real_value_pilot_status.v0",
pilotId: `0x${"a".repeat(64)}`,
label: "FlowChain capped owner real-value pilot",
state: "degraded",
stateReason: "Only mock/local/Base Sepolia bridge observations are visible.",
baseChainId: 8453,
cappedOwnerTesting: true,
broadPublicReadiness: false,
productionReady: false,
browserStoresSecrets: false,
nextOperatorStep: {
label: "Observe Base 8453 deposit",
command: "npm run bridge:observe -- --mode base-mainnet-canary --acknowledge-real-funds --max-usd 25",
reason: "No Base 8453 pilot deposit has been loaded.",
},
lifecycle: [{
phase: "base_deposit_observed",
state: "degraded",
title: "Observe Base 8453 deposit",
summary: "No Base 8453 pilot deposit has been loaded.",
nextOperatorCommand: "npm run bridge:observe -- --mode base-mainnet-canary --acknowledge-real-funds --max-usd 25",
}],
capStatus: { state: "degraded", withinCap: true, productionReady: false },
pauseStatus: { state: "live", status: "unpaused", productionReady: false },
retryStatus: { state: "live", duplicateReplayKeys: [], productionReady: false },
emergencyStatus: { state: "live", status: "standby", productionReady: false },
},
},
devnetState,
devnetDashboardState,
Expand All @@ -171,6 +201,8 @@ describe("dashboard fixture", () => {
expect(workbench.node.status).toBe("verified");
expect(workbench.sections.blocks[0].provenance.origin).toBe("local");
expect(workbench.sections.blocks[0].provenance.localPathHint).toBe("http://127.0.0.1:8787");
expect(workbench.sections.realValuePilot[0].title).toBe("Pilot degraded");
expect(workbench.sections.realValuePilot[0].summary).toContain("Only mock/local/Base Sepolia");
expect(workbench.sections.provenance.find((record) => record.id === "control-plane-api")?.status).toBe("verified");
});

Expand Down Expand Up @@ -201,6 +233,23 @@ describe("dashboard fixture", () => {
if (url.endsWith("/state")) {
return Response.json({ state: devnetState });
}
if (url.endsWith("/pilot/status")) {
return Response.json({
schema: "flowmemory.control_plane.real_value_pilot_status.v0",
state: "degraded",
label: "FlowChain capped owner real-value pilot",
stateReason: "Waiting for Base 8453 deposit.",
baseChainId: 8453,
cappedOwnerTesting: true,
broadPublicReadiness: false,
productionReady: false,
browserStoresSecrets: false,
nextOperatorStep: {
command: "npm run bridge:observe -- --mode base-mainnet-canary --acknowledge-real-funds --max-usd 25",
},
lifecycle: [],
});
}
if (url === WORKBENCH_DEVNET_STATE_PATH) {
return Response.json(devnetState);
}
Expand All @@ -220,10 +269,12 @@ describe("dashboard fixture", () => {
expect(workbench.source).toBe("control-plane");
expect(workbench.raw.controlPlaneHealth).toEqual({ status: "ok" });
expect(workbench.raw.controlPlaneState).toEqual({ state: devnetState });
expect(workbench.raw.controlPlanePilotStatus).toMatchObject({ state: "degraded" });
expect(workbench.raw.devnetState).toEqual(devnetState);
expect(workbench.raw.bridgeTestDeposit).toEqual(bridgeTestDeposit);
expect(workbench.loadIssues).toEqual([]);
expect(fetchMock).toHaveBeenCalledWith("http://127.0.0.1:8787/health", expect.any(Object));
expect(fetchMock).toHaveBeenCalledWith("http://127.0.0.1:8787/pilot/status", expect.any(Object));
expect(fetchMock).toHaveBeenCalledWith(WORKBENCH_DEVNET_STATE_PATH, expect.any(Object));
expect(fetchMock).toHaveBeenCalledWith(WORKBENCH_BRIDGE_TEST_DEPOSIT_PATH, expect.any(Object));
});
Expand All @@ -239,6 +290,9 @@ describe("dashboard fixture", () => {
expect(html).toContain("Local explorer workbench");
expect(html).toContain("Node and API status");
expect(html).toContain("Control-plane offline");
expect(html).toContain("Real-value pilot");
expect(html).toContain("capped owner testing");
expect(html).toContain("public readiness");
expect(html).toContain("Wallet Metadata");
expect(html).toContain("Token Launch");
expect(html).toContain("Token Balances");
Expand Down
Loading
Loading