Skip to content
Draft
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
15 changes: 15 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,21 @@ A Cloudflare Workers-based strategy switch console. The daily pages are the Reac

**Dependency boundary:** The web app consumes generated assets (`config.js`, `strategy_profiles_asset.js`) and reads `platform-config.json` indirectly via the Worker API. It does **not** depend on Python scripts at runtime.

The cockpit overview and health pages share `AuditHistory`, which follows the
audit API's opaque `next_cursor` and deduplicates event IDs across pages. Refresh
discards the old traversal; a failed continuation preserves loaded events and
offers retry. Malformed or partial payloads cannot become a successful empty
history. The adapter consumes `id`, `actor_alias`, structured `subject` and
nullable string `revision` from `qsl.audit_events.v1`.

The account overview reads `/api/runtime/readback?scope=platform:account-alias`.
It displays the observed runtime switch, scheduler state and producer timestamp
from `qsl.runtime_readback.v1`. Stale or unavailable observations remain unknown;
configuration, decision receipts and application status do not substitute for
runtime readback. These reads add no mutation or trading authority. Until the
backend endpoints are available, 404/501 responses display unavailable and
transport/server failures display a retryable read error.

---

## CI/CD: Independent Validation
Expand Down
162 changes: 162 additions & 0 deletions tests/console_cockpit_decision_validation.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import assert from "node:assert/strict";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import { build } from "../web/strategy-switch-console/frontend/node_modules/esbuild/lib/main.js";

const root = fileURLToPath(new URL("../web/strategy-switch-console/frontend/", import.meta.url));
const directory = await mkdtemp(join(tmpdir(), "cockpit-decision-"));
try {
const outfile = join(directory, "cockpit.cjs");
await build({
stdin: {
resolveDir: root,
contents: `
import { createElement as h } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { I18nProvider } from './src/i18n';
import { actionBlockers, expiryState, MutationOutcomeUnknown } from './src/api/cockpit';
import { ApplicationStatus, DigestStatus, EffectivePolicyPanel, RiskTierSelector } from './src/components/cockpit';
export { actionBlockers, expiryState, MutationOutcomeUnknown };
const wrap = (language, node) => renderToStaticMarkup(h(I18nProvider, { language }, node));
export const tiers = (language) => wrap(language, h(RiskTierSelector, {
scope: 'demo_account', value: 'BALANCED_COMPOUNDING', savedValue: 'BALANCED_COMPOUNDING',
selectionRevision: 12, mappingAvailable: true, disabled: false, saving: false, dirty: false,
onChange: () => {}, onSave: () => {},
}));
const binding = {
scope: 'demo_account', selected_tier: 'GROWTH_COMPOUNDING', selection_revision: 12,
approved_tier: null, approved_policy_id: null, approved_policy_revision: null,
effective_tier: 'BALANCED_COMPOUNDING', effective_policy_id: 'demo-balanced-v3',
effective_policy_revision: 'r7', effective_observed_at: '2026-11-01T09:10:00Z',
temporary_restriction: { active: true, reason: 'Reduction only', until: null },
values: [{ key: 'max_gross_exposure', label_key: null, value: 45, unit: 'percent', status: 'known', source: 'server' }],
};
export const policy = (language) => wrap(language, h(EffectivePolicyPanel, { binding }));
const base = {
operation_id: 'op-1', request_id: 'req-1', decision_id: 'decision-1', decision_revision: 3,
action: 'approve', title_key: null, account_alias: null, platform: null, release_id: null,
target_id: null, state: 'applied', recorded_at: null, dispatched_at: null,
applied_at: '2026-11-01T10:00:05Z', applied_by: 'platform', runtime_confirmed_at: null,
runtime_state: 'unknown', failure_reason: null, unknown_reason: null,
readback_available: null, server_time: null,
};
export const applied = (language) => wrap(language, h(ApplicationStatus, { operation: base, status: 'ready' }));
export const confirmed = (language) => wrap(language, h(ApplicationStatus, {
operation: { ...base, runtime_confirmed_at: '2026-11-01T10:05:00Z', runtime_state: 'running' }, status: 'ready',
}));
export const operationMissing = (language) => wrap(language, h(ApplicationStatus, { operation: null, status: 'not_ready' }));
export const digestAccepted = (language) => wrap(language, h(DigestStatus, {
digest: {
business_day: '2026-11-01', timezone: 'America/New_York', expected_targets: 4,
covered_targets: 3, unavailable_targets: 1, delivery_state: 'accepted',
accepted_at: '2026-11-01T13:05:00Z', acknowledged_at: null,
source_as_of: '2026-11-01T13:00:00Z', legacy_receipts: false, targets: [],
},
}));
`,
},
bundle: true,
platform: "node",
format: "cjs",
outfile,
jsx: "automatic",
loader: { ".css": "empty" },
define: { "import.meta.env": "{}" },
logLevel: "silent",
});
const ui = createRequire(import.meta.url)(outfile);

// Expiry is derived from the server clock plus elapsed time only.
const received = "2026-11-01T10:00:00Z";
const receivedMs = Date.parse(received);
assert.equal(ui.expiryState("2026-11-01T10:30:00Z", "2026-11-01T10:00:00Z", received, receivedMs).state, "open");
assert.equal(ui.expiryState("2026-11-01T10:30:00Z", "2026-11-01T10:00:00Z", received, receivedMs + 31 * 60_000).state,
"expired", "a forward browser clock cannot extend server validity");
assert.equal(ui.expiryState("2026-11-01T10:30:00Z", "2026-11-01T10:00:00Z", received, receivedMs - 60 * 60_000).state,
"open", "a backward browser clock cannot expire a decision early");
assert.equal(ui.expiryState("2026-11-01T10:30:00Z", null, received, receivedMs).state, "unknown",
"without a server clock the boundary stays unconfirmed");
assert.equal(ui.expiryState(null, null, received, receivedMs).state, "none");

const decision = {
state: "pending", allowed_actions: ["approve"], expires_at: null, revision: 2,
policy: { selected_tier: "BALANCED_COMPOUNDING", policy_revision: "r7" },
evidence: { state: "current", qualification: "qualified", limitations: [] },
};
const view = { serverTime: "2026-11-01T10:00:00Z", receivedAt: received, now: receivedMs };
assert.deepEqual(ui.actionBlockers(decision, view), []);
assert.ok(ui.actionBlockers({ ...decision, evidence: { ...decision.evidence, state: "stale" } }, view).includes("evidence_stale"),
"stale supporting data blocks approval");
assert.ok(ui.actionBlockers({ ...decision, evidence: { ...decision.evidence, qualification: "unknown" } }, view).includes("evidence_unqualified"));
assert.ok(ui.actionBlockers({ ...decision, allowed_actions: [] }, view).includes("approval_not_allowed"),
"the server allow-list stays authoritative");
assert.ok(ui.actionBlockers({ ...decision, policy: null }, view).includes("policy_unavailable"));
assert.ok(ui.actionBlockers({ ...decision, policy: { selected_tier: null, policy_revision: null } }, view).includes("policy_binding_missing"));

const unknown = new ui.MutationOutcomeUnknown("req-1", "op-1");
assert.equal(unknown.requestId, "req-1");
assert.equal(unknown.operationId, "op-1");
assert.equal(unknown.message, "mutation_outcome_unknown");

// Selected preference, approved mapping and observed policy stay separate.
const policyEn = ui.policy("en");
assert.match(policyEn, /Growth compounding/, "the selected preference is shown");
assert.match(policyEn, /Balanced compounding/, "the observed policy is not overwritten by the selection");
assert.match(policyEn, /Reduction only/, "a temporary restriction is shown separately");
assert.match(policyEn, /Policy mapping unavailable/, "a missing approved mapping stays unavailable");

// Applied is not runtime-confirmed.
const applied = ui.applied("en");
assert.match(applied, /Platform reported applied; runtime confirmation pending/);
assert.doesNotMatch(applied, /Runtime confirmed/);
assert.match(ui.confirmed("en"), /Runtime confirmed/);
assert.match(ui.operationMissing("en"), /operation-status endpoint is not available/);

// Accepted delivery is not delivered, and coverage stays visible.
const digest = ui.digestAccepted("en");
assert.match(digest, /Accepted for delivery/);
assert.match(digest, /Targets 4 · covered 3 · unavailable 1/);
assert.match(digest, /Accepted is not delivered/);
assert.match(ui.tiers("zh"), /平衡复利/);
assert.match(ui.tiers("en"), /checked/);
assert.match(ui.tiers("en"), /No tier selected/, "clearing a tier preference stays possible");

// The promotion request no longer carries a client-chosen tier.
const decisions = await readFile(new URL("../web/strategy-switch-console/frontend/src/pages/decisions/DecisionsPage.tsx", import.meta.url), "utf8");
assert.doesNotMatch(decisions, /CAPITAL_PRESERVATION/, "the promotion request must not hard-code a tier");
assert.match(decisions, /promotionRiskBinding/);
assert.match(decisions, /risk\.policy_revision/);
assert.match(decisions, /allowed_actions/, "server allow-lists gate the client controls");

// Pages consume the cockpit surfaces instead of restating them.
const sources = await Promise.all([
"src/pages/overview/OverviewPage.tsx",
"src/pages/strategies/StrategiesPage.tsx",
"src/pages/accounts/AccountDetail.tsx",
"src/pages/accounts/RiskPreference.tsx",
"src/pages/health/HealthPage.tsx",
].map(file => readFile(new URL(`../web/strategy-switch-console/frontend/${file}`, import.meta.url), "utf8")));
const [overview, strategies, accountDetail, riskPreference, health] = sources;
assert.match(overview, /function DecisionQueue/);
assert.match(overview, /queue_incomplete/, "a partial queue hides reassuring counts");
assert.match(overview, /AuditHistory/);
const activity = await readFile(join(root, "src/components/AuditHistory.tsx"), "utf8");
assert.match(activity, /loadAuditEvents\(cursor\)/);
assert.match(activity, /next_cursor/);
assert.match(strategies, /loadLifecycles/);
assert.match(strategies, /LifecycleStepper/);
assert.match(strategies, /binding\.present/, "absent lifecycle records stay absent");
assert.match(accountDetail, /loadPolicyBinding/);
assert.match(accountDetail, /EffectivePolicyPanel/);
assert.match(riskPreference, /RiskTierSelector/);
assert.doesNotMatch(riskPreference, /<select/, "the tier picker is no longer a bare select");
assert.match(health, /loadDigestStatus/);
assert.match(health, /DigestStatus/);
assert.match(health, /AuditHistory/);
} finally {
await rm(directory, { recursive: true, force: true });
}
console.log("console_cockpit_decision_validation: PASS");
87 changes: 87 additions & 0 deletions tests/console_cockpit_readback_validation.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import assert from "node:assert/strict";
import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import { build } from "../web/strategy-switch-console/frontend/node_modules/esbuild/lib/main.js";

const root = fileURLToPath(new URL("../web/strategy-switch-console/frontend/", import.meta.url));
const directory = await mkdtemp(join(tmpdir(), "cockpit-readback-"));
const originalFetch = globalThis.fetch, originalWindow = globalThis.window;
try {
const outfile = join(directory, "readback.cjs");
await build({ stdin: { resolveDir: root, contents: `
import { createElement as h } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { I18nProvider } from './src/i18n';
import { AuditTimeline } from './src/components/cockpit';
import { RuntimeReadbackDetails } from './src/components/RuntimeReadbackPanel';
export { loadAuditEvents, loadRuntimeReadback } from './src/api/cockpit';
export { mergeAuditEvents } from './src/components/AuditHistory';
const wrap = node => renderToStaticMarkup(h(I18nProvider, { language: 'en' }, node));
export const timeline = events => wrap(h(AuditTimeline, { events, status: 'ready', hasMore: true, onMore() {} }));
export const runtime = value => wrap(h(RuntimeReadbackDetails, { value }));
` }, bundle: true, platform: "node", format: "cjs", outfile, logLevel: "silent" });
const api = createRequire(import.meta.url)(outfile);
const event = { id: "durable_human_decision:demo", source: "durable_human_decision", occurred_at: "2026-11-01T00:00:00.000Z",
actor_alias: "user-demo", action: "record_owner_decision", subject: { kind: "owner_decision", id: "demo" },
revision: "demo-revision", outcome: "decided", ref: null };
const page = { schema_version: "qsl.audit_events.v1", data_status: "ready", events: [event], next_cursor: "opaque_cursor",
coverage: "durable_and_retained", errors: [], server_time: "2026-11-01T00:01:00.000Z" };
let path, method;
const respond = (body, status = 200) => { globalThis.fetch = async (url, options) => {
path = url; method = options.method; return Response.json(body, { status });
}; };
respond(page);
let read = await api.loadAuditEvents();
assert.equal(method, "GET");
assert.equal(path, "/api/audit-events?limit=25");
assert.equal(read.data.next_cursor, "opaque_cursor");
assert.equal(read.data.events[0].event_id, event.id);
assert.equal(read.data.events[0].actor, event.actor_alias);
assert.equal(read.data.events[0].subject, "owner_decision: demo");
assert.equal(read.data.events[0].revision, "demo-revision");
assert.match(api.timeline(read.data.events), /Load more/);
const first = read.data.events;
respond({ ...page, events: [{ ...event, id: "older" }], next_cursor: null });
read = await api.loadAuditEvents("opaque_cursor");
assert.equal(path, "/api/audit-events?limit=25&cursor=opaque_cursor");
assert.equal(read.data.next_cursor, null);
assert.deepEqual(api.mergeAuditEvents(first, [...first, ...read.data.events]).map(row => row.event_id), [event.id, "older"]);
for (const malformed of [ {}, { ...page, data_status: "partial" }, { ...page, events: null },
{ ...page, next_cursor: undefined }, { ...page, events: [{}] }, { ...page, events: [event, event] },
{ ...page, errors: ["source_unavailable"] }, { ...page, next_cursor: "" }, { ...page, events: [] } ]) {
respond(malformed); await assert.rejects(api.loadAuditEvents(), /invalid_|duplicate_/);
}
respond(page); await assert.rejects(api.loadAuditEvents("opaque_cursor"), /invalid_audit_page/);
respond({}, 404); assert.equal((await api.loadAuditEvents()).status, "not_ready");
respond({}, 503); await assert.rejects(api.loadAuditEvents());
globalThis.window = new EventTarget();
let invalidations = 0;
window.addEventListener("qsl-private-session-invalid", () => invalidations++);
for (const status of [401, 403]) { respond({}, status); await assert.rejects(api.loadAuditEvents()); }
assert.equal(invalidations, 2);
const runtime = { server_time: "2026-11-01T00:01:00.000Z", schema_version: "qsl.runtime_readback.v1", scope: "longbridge:demo", data_status: "ready",
observed_at: event.occurred_at, runtime_enabled: false, scheduler_state: "paused", strategy_profile: "demo",
source: "stored_runtime_target_lifecycle", policy: { read_only: true, no_order: true, execution_authority_granted: false } };
respond(runtime); read = await api.loadRuntimeReadback(runtime.scope);
assert.equal(path, "/api/runtime/readback?scope=longbridge%3Ademo");
assert.equal(read.data.runtime_enabled, false);
assert.match(api.runtime(read.data), /Disabled/);
respond({ ...runtime, data_status: "stale", runtime_enabled: null, scheduler_state: "unknown", strategy_profile: null });
read = await api.loadRuntimeReadback(runtime.scope);
assert.equal(read.data.runtime_enabled, null);
assert.match(api.runtime(read.data), /Unknown/);
assert.doesNotMatch(api.runtime(read.data), /Disabled/);
for (const malformed of [{ ...runtime, scope: "longbridge:other" }, { ...runtime, data_status: "stale" },
{ ...runtime, observed_at: null }, { ...runtime, runtime_enabled: "false" }, { ...runtime, policy: {} }, {}]) {
respond(malformed); await assert.rejects(api.loadRuntimeReadback(runtime.scope), /invalid_runtime_readback/);
}
respond({}, 503); await assert.rejects(api.loadRuntimeReadback(runtime.scope));
respond({}, 404); assert.equal((await api.loadRuntimeReadback(runtime.scope)).status, "not_ready");
console.log("Cockpit audit and runtime readback validation passed");
} finally {
globalThis.fetch = originalFetch; globalThis.window = originalWindow;
await rm(directory, { recursive: true, force: true });
}
12 changes: 10 additions & 2 deletions tests/console_display_gaps_validation.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,16 @@ try {
define: { "import.meta.env": "{}" }, plugins: [{ name: "controlled-read", setup(b) {
b.onLoad({ filter: /\/lib\/hooks\.ts$/ }, () => ({ contents:
"export const useApi = () => globalThis.__displayState;", loader: "js" }));
b.onLoad({ filter: /\/api\/client\.ts$/ }, () => ({ contents:
"export async function getJson() { if (globalThis.__displayError) throw Error('synthetic_read_failure'); return globalThis.__displayPayload; } export const readRevision = () => 0; export const postJson = () => { throw Error('writes_forbidden'); };", loader: "js" }));
b.onLoad({ filter: /\/api\/client\.ts$/ }, () => ({ contents: `
export class AccessError extends Error {
constructor(status) { super(status === 401 ? 'session_expired' : 'access_denied'); this.status = status; }
}
export class HttpError extends Error {
constructor(status, payload) { super(payload?.error || 'http_' + status); this.status = status; this.payload = payload; }
}
export async function getJson() { if (globalThis.__displayError) throw Error('synthetic_read_failure'); return globalThis.__displayPayload; }
export const readRevision = () => 0;
export const postJson = () => { throw Error('writes_forbidden'); };`, loader: "js" }));
for (const [file, exports] of [["StrategiesPage", "CatalogTab, ResearchTab, parseResearchLeads, loadResearchLeads"],
["HealthPage", "StatStrip, CredentialsCard"], ["RunGrid", "DayDetail"]]) {
b.onLoad({ filter: new RegExp(`/${file}\\.tsx$`) }, async ({ path }) => ({
Expand Down
Loading
Loading