docs: document qa runtime facade contracts

This commit is contained in:
Peter Steinberger
2026-06-04 22:40:12 -04:00
parent 2f00fbf28e
commit d23558e691
2 changed files with 35 additions and 0 deletions

View File

@@ -97,53 +97,69 @@ function loadFacadeModule(): FacadeModule {
});
}
/** Build a QA bus target string from conversation and optional thread parts. */
export const buildQaTarget: FacadeModule["buildQaTarget"] = ((...args) =>
loadFacadeModule().buildQaTarget(...args)) as FacadeModule["buildQaTarget"];
/** Format a QA bus target string for display and CLI output. */
export const formatQaTarget: FacadeModule["buildQaTarget"] = ((...args) =>
loadFacadeModule().buildQaTarget(...args)) as FacadeModule["buildQaTarget"];
/** Create a QA bus thread through the bundled QA channel facade. */
export const createQaBusThread: FacadeModule["createQaBusThread"] = ((...args) =>
loadFacadeModule().createQaBusThread(...args)) as FacadeModule["createQaBusThread"];
/** Delete a QA bus message through the bundled QA channel facade. */
export const deleteQaBusMessage: FacadeModule["deleteQaBusMessage"] = ((...args) =>
loadFacadeModule().deleteQaBusMessage(...args)) as FacadeModule["deleteQaBusMessage"];
/** Edit a QA bus message through the bundled QA channel facade. */
export const editQaBusMessage: FacadeModule["editQaBusMessage"] = ((...args) =>
loadFacadeModule().editQaBusMessage(...args)) as FacadeModule["editQaBusMessage"];
/** Read the current QA bus state snapshot. */
export const getQaBusState: FacadeModule["getQaBusState"] = ((...args) =>
loadFacadeModule().getQaBusState(...args)) as FacadeModule["getQaBusState"];
/** Inject an inbound QA bus message for channel and gateway tests. */
export const injectQaBusInboundMessage: FacadeModule["injectQaBusInboundMessage"] = ((...args) =>
loadFacadeModule().injectQaBusInboundMessage(
...args,
)) as FacadeModule["injectQaBusInboundMessage"];
/** Normalize a user-provided QA target string when possible. */
export const normalizeQaTarget: FacadeModule["normalizeQaTarget"] = ((...args) =>
loadFacadeModule().normalizeQaTarget(...args)) as FacadeModule["normalizeQaTarget"];
/** Parse a QA target string into chat type, conversation id, and optional thread id. */
export const parseQaTarget: FacadeModule["parseQaTarget"] = ((...args) =>
loadFacadeModule().parseQaTarget(...args)) as FacadeModule["parseQaTarget"];
/** Poll the QA bus for new messages from a cursor. */
export const pollQaBus: FacadeModule["pollQaBus"] = ((...args) =>
loadFacadeModule().pollQaBus(...args)) as FacadeModule["pollQaBus"];
/** Lazy QA channel plugin object used by plugin loader tests. */
export const qaChannelPlugin: FacadeModule["qaChannelPlugin"] = createLazyFacadeObjectValue(
() => loadFacadeModule().qaChannelPlugin,
);
/** Add a reaction to a QA bus message. */
export const reactToQaBusMessage: FacadeModule["reactToQaBusMessage"] = ((...args) =>
loadFacadeModule().reactToQaBusMessage(...args)) as FacadeModule["reactToQaBusMessage"];
/** Read one QA bus message by id. */
export const readQaBusMessage: FacadeModule["readQaBusMessage"] = ((...args) =>
loadFacadeModule().readQaBusMessage(...args)) as FacadeModule["readQaBusMessage"];
/** Search QA bus messages using the bundled channel facade. */
export const searchQaBusMessages: FacadeModule["searchQaBusMessages"] = ((...args) =>
loadFacadeModule().searchQaBusMessages(...args)) as FacadeModule["searchQaBusMessages"];
/** Send an outbound QA bus message with optional attachments and tool calls. */
export const sendQaBusMessage: FacadeModule["sendQaBusMessage"] = ((...args) =>
loadFacadeModule().sendQaBusMessage(...args)) as FacadeModule["sendQaBusMessage"];
/** Install a test runtime implementation into the bundled QA channel facade. */
export const setQaChannelRuntime: FacadeModule["setQaChannelRuntime"] = ((...args) =>
loadFacadeModule().setQaChannelRuntime(...args)) as FacadeModule["setQaChannelRuntime"];

View File

@@ -30,6 +30,7 @@ function isMissingQaRuntimeError(error: unknown) {
);
}
/** Load the bundled QA lab runtime surface, throwing when the private bundle is absent. */
export function loadQaRuntimeModule(): QaRuntimeSurface {
const env = resolvePrivateQaBundledPluginsEnv();
return loadBundledPluginPublicSurfaceModuleSync<QaRuntimeSurface>({
@@ -39,6 +40,7 @@ export function loadQaRuntimeModule(): QaRuntimeSurface {
});
}
/** Check whether the bundled QA lab runtime surface is present without hiding other load errors. */
export function isQaRuntimeAvailable(): boolean {
try {
loadQaRuntimeModule();
@@ -51,6 +53,7 @@ export function isQaRuntimeAvailable(): boolean {
}
}
/** Normalized options passed from live-transport QA CLIs into lane runners. */
export type LiveTransportQaCommandOptions = {
repoRoot?: string;
outputDir?: string;
@@ -85,16 +88,19 @@ type LiveTransportQaCommanderOptions = {
credentialRole?: string;
};
/** Commander registration hook for one live-transport QA subcommand. */
export type LiveTransportQaCliRegistration = {
commandName: string;
register(qa: Command): void;
};
/** Help text customizations for live credential source and role flags. */
export type LiveTransportQaCredentialCliOptions = {
sourceDescription?: string;
roleDescription?: string;
};
/** Declarative command metadata and runner used to install a live-transport QA CLI. */
export type LiveTransportQaCliRegistrationOptions = {
commandName: string;
credentialOptions?: LiveTransportQaCredentialCliOptions;
@@ -111,6 +117,7 @@ export type LiveTransportQaCliRegistrationOptions = {
run: (opts: LiveTransportQaCommandOptions) => Promise<void>;
};
/** Memoize a lazy CLI runtime import so repeated command paths share one loaded module. */
export function createLazyCliRuntimeLoader<T>(load: () => Promise<T>) {
let promise: Promise<T> | null = null;
return async () => {
@@ -195,6 +202,7 @@ function registerLiveTransportQaCli(
});
}
/** Build a Commander registration object for one live-transport QA command. */
export function createLiveTransportQaCliRegistration(
params: LiveTransportQaCliRegistrationOptions,
): LiveTransportQaCliRegistration {
@@ -209,12 +217,14 @@ export function createLiveTransportQaCliRegistration(
};
}
/** One top-level check row in a rendered QA markdown report. */
export type QaReportCheck = {
name: string;
status: "pass" | "fail" | "skip";
details?: string;
};
/** One scenario section in a rendered QA markdown report. */
export type QaReportScenario = {
name: string;
status: "pass" | "fail" | "skip";
@@ -231,12 +241,14 @@ export {
type LiveTransportStandardScenarioId,
} from "./qa-live-transport-scenarios.js";
/** Docker command runner abstraction used by QA Docker helpers and tests. */
export type QaDockerRunCommand = (
command: string,
args: string[],
cwd: string,
) => Promise<{ stdout: string; stderr: string }>;
/** Minimal fetch-like health probe used by QA Docker runtime helpers. */
export type QaDockerFetchLike = (input: string) => Promise<{ ok: boolean }>;
const DEFAULT_QA_DOCKER_COMMAND_TIMEOUT_MS = 120_000;
@@ -250,6 +262,7 @@ function pushQaReportDetailsBlock(lines: string[], label: string, details: strin
lines.push("", "```text", details, "```");
}
/** Render checks, scenarios, timeline, and notes into the standard QA markdown report format. */
export function renderQaMarkdownReport(params: {
title: string;
startedAt: Date;
@@ -329,10 +342,12 @@ export function renderQaMarkdownReport(params: {
return lines.join("\n");
}
/** Append a formatted live-lane issue while preserving the caller-owned issue list. */
export function appendQaLiveLaneIssue(issues: string[], label: string, error: unknown) {
issues.push(`${label}: ${formatErrorMessage(error)}`);
}
/** Format a live-lane failure message that includes artifact labels and paths. */
export function buildQaLiveLaneArtifactsError(params: {
heading: string;
artifacts: Record<string, string>;
@@ -346,6 +361,7 @@ export function buildQaLiveLaneArtifactsError(params: {
].join("\n");
}
/** Print live-transport QA artifact paths with a lane label for CI log parsers. */
export function printLiveTransportQaArtifacts(
laneLabel: string,
artifacts: Record<string, string>,
@@ -397,6 +413,7 @@ async function findFreeQaDockerPort() {
});
}
/** Return the preferred Docker host port unless it is unpinned and already occupied. */
export async function resolveQaDockerHostPort(preferredPort: number, pinned: boolean) {
if (pinned || (await isQaDockerPortFree(preferredPort))) {
return preferredPort;
@@ -471,6 +488,7 @@ async function isQaDockerHealthy(url: string, fetchImpl: QaDockerFetchLike) {
}
}
/** Create Docker command, health-check, and compose helpers for QA harnesses. */
export function createQaDockerRuntime(params: {
auditContext: string;
commandTimeoutMs?: number | null;
@@ -639,6 +657,7 @@ export function createQaDockerRuntime(params: {
type ProcessWriteCallback = (err?: Error | null) => void;
/** Tee stdout and stderr into a private artifact file until the returned stop hook runs. */
export async function startLiveTransportQaOutputTee(params: {
fileName: string;
outputDir: string;