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
21 changes: 13 additions & 8 deletions role-model-router/apps/runtime-host-bridge/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1012,10 +1012,9 @@ export async function main(): Promise<void> {
// available at runtime.
const currentPostObservationOperations = (): ReturnType<typeof createTrackBOperations> | null =>
postObservationOperations;
const drainPostObservationOutbox = async (
runtime: Awaited<ReturnType<typeof createProductionExtensionRuntime>>,
) =>
postObservationOutbox.drain((observation) => {
const postObservationHandler =
(runtime: Awaited<ReturnType<typeof createProductionExtensionRuntime>>) =>
(observation: Parameters<typeof runTrackBPostObservation>[1]) => {
const processingInput = {
scope: options.scopeId,
channel: packagedProfile?.channel ?? "development",
Expand All @@ -1036,7 +1035,10 @@ export async function main(): Promise<void> {
(aggregate) => operations.recordContributionAggregate(aggregate),
)
: runTrackBPostObservation(runtime, observation, processingInput);
});
};
const drainPostObservationOutbox = async (
runtime: Awaited<ReturnType<typeof createProductionExtensionRuntime>>,
) => postObservationOutbox.drain(postObservationHandler(runtime));
const createBackend = async (
trackBOperationsEndpoint?: string,
trackBOperationsToken?: string,
Expand Down Expand Up @@ -1097,14 +1099,17 @@ export async function main(): Promise<void> {
readTrackBExtensionReadback: async (body) => {
const requestId = String(body.requestId ?? "").trim();
if (!requestId) throw new Error("Track B extension readback requestId is required");
const receipt = await postObservationOutbox.readReceipt(requestId);
const runtime = extensionRuntimeRef.current;
if (!runtime) throw new Error("Track B extension runtime is unavailable");
const receipt = await postObservationOutbox.drainUntilReceipt(
requestId,
postObservationHandler(runtime),
);
if (!receipt) throw new Error(`Track B observation receipt not found: ${requestId}`);
const result = receipt.result as Record<string, unknown>;
const closure = result.extensionClosure as TrackBExtensionClosure | undefined;
if (!closure)
throw new Error(`Track B observation has no extension closure: ${requestId}`);
const runtime = extensionRuntimeRef.current;
if (!runtime) throw new Error("Track B extension runtime is unavailable");
return verifyTrackBExtensionClosureAfterRestart(runtime, closure, {
channel: packagedProfile?.channel ?? "development",
scope: options.scopeId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1792,6 +1792,15 @@ export function createTrackBPostObservationOutbox({
}
});
},
async drainUntilReceipt(
requestId: string,
handler: (observation: TrackBPostObservationWorkItem) => Promise<unknown>,
): Promise<TrackBPostObservationReceipt | null> {
const existing = await this.readReceipt(requestId);
if (existing) return existing;
await this.drain(handler);
return this.readReceipt(requestId);
},
async read(): Promise<{
readonly pendingCount: number;
readonly receiptCount: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,39 @@ test("GREEN: post-observation outbox is a normalized SQLite authority with bound
afterDrain.close();
});

test("GREEN: a readback-driven retry drains a transiently failed pending observation without another routed request", async () => {
const root = await import("node:fs/promises").then(({ mkdtemp }) =>
mkdtemp(path.join(os.tmpdir(), "run95-readback-retry-")),
);
roots.push(root);
const outbox = createTrackBPostObservationOutbox({
filePath: path.join(root, "post-observation-outbox.sqlite"),
maxItems: 8,
});
await outbox.enqueue(identity("retry-without-next-route"));
await expect(
outbox.drain(async () => {
throw new Error("temporary private operation timeout");
}),
).rejects.toThrow(/temporary private operation timeout/);
expect(await outbox.read()).toMatchObject({ pendingCount: 1, receiptCount: 0 });

const recovered = await (
outbox as unknown as {
drainUntilReceipt(
requestId: string,
handler: (item: Record<string, unknown>) => Promise<unknown>,
): Promise<{ requestId: string } | null>;
}
).drainUntilReceipt("retry-without-next-route", async (item) => ({
status: "recovered",
extensionClosure: { requestId: item.requestId },
}));

expect(recovered).toMatchObject({ requestId: "retry-without-next-route" });
expect(await outbox.read()).toMatchObject({ pendingCount: 0, receiptCount: 1 });
});

test("GREEN: imports N-1 JSON once, classifies every legacy row, and quarantines malformed rows", async () => {
const root = await import("node:fs/promises").then(({ mkdtemp }) =>
mkdtemp(path.join(os.tmpdir(), "run94-sp5-legacy-")),
Expand Down
Loading