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
81 changes: 65 additions & 16 deletions role-model-router/apps/runtime-host-bridge/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,29 @@ export function readCaptureRoleId(capture: DurableReplayCapture): string | undef
return typeof roleId === "string" && roleId.trim() ? roleId.trim() : undefined;
}

/**
* Run 106 (live finding on the dev replay queue, 2026-10-07): the task family the capture was classified
* under, read from the same two places as the role and the revision. The learner's floor and its pack
* scope are keyed by (roleId, taskTypeId) and a comparison whose comparability block names only the role
* is excluded fail-closed as `incomplete_scope` - the comparison group is finalized, the floor never
* counts it, and no pack can ever be written for the task.
*
* `roleId` and `taxonomyVersion` have always been read through a classification fallback. `taskTypeId`
* was read straight off the capture record, so it worked only while the capture writer also stamped the
* redundant top-level copy; a capture that records the family ONLY where the contract puts it - the
* classification - produced an unscoped comparison. Read it exactly like its two siblings.
*/
export function readCaptureTaskTypeId(capture: DurableReplayCapture): string | undefined {
const direct = capture.taskTypeId;
if (typeof direct === "string" && direct.trim()) return direct.trim();
const classification = capture.classification;
if (!classification || typeof classification !== "object" || Array.isArray(classification)) {
return undefined;
}
const taskTypeId = (classification as Readonly<Record<string, unknown>>).taskTypeId;
return typeof taskTypeId === "string" && taskTypeId.trim() ? taskTypeId.trim() : undefined;
}

/**
* Run 98 addendum 58 §23 (live v316, 08:33Z): Replay Core re-presents an `append_recovery` request when a
* previous attempt already burned the nonce and persisted the provider receipt but never appended the
Expand Down Expand Up @@ -2430,11 +2453,13 @@ export function createSupervisedReplayEvaluationCompleter(input: {
// Run 99 R33 (S34 live finding): every comparison in the live store carried no task family
// because this supervised-replay path never passed the capture's family, so the learner's
// family-scoped floor could never be met from real traffic. The capture records the family
// (`addendum 19 S33`), so it travels with the replay.
...(typeof input.sourceCapture.taskTypeId === "string" &&
input.sourceCapture.taskTypeId.trim()
? { taskTypeId: input.sourceCapture.taskTypeId.trim() }
: {}),
// (`addendum 19 S33`), so it travels with the replay - read through the same classification
// fallback the role and the revision already use (Run 106), because a capture that records the
// family only on its classification produced a comparison the floor excludes as incomplete_scope.
...(() => {
const taskTypeId = readCaptureTaskTypeId(input.sourceCapture);
return taskTypeId ? { taskTypeId } : {};
})(),
// Run 98 addendum 58 §22.2.3: the revision the capture was classified under travels with the
// family, so the comparability key — and the pack scope the learner derives from it — is
// version-stamped and the advisory's version gate can match it instead of failing closed.
Expand Down Expand Up @@ -7409,6 +7434,15 @@ export async function main(): Promise<void> {
ledger,
policySet,
configuredEndpointIds: endpoints,
/**
* Run 106: the same descriptors the supervised-replay path already builds from the registry
* (`configuredReplayArms`), handed to the loop so the focus-dispatch walk can prefer a
* counterfactual arm that runs at the source capture's reasoning effort. Without it the arm is
* effort-mismatched whenever the source's own model is the arm's model, the comparison is finalized
* `arm_effort_mismatch`, and the admission floor discards it - a replay and a paid provider call
* spent on evidence nothing can use.
*/
configuredEndpointDescriptors: endpointDescriptors,
healthyEndpointIds: healthyEndpoints,
/**
* Run 105 R8/R9/R11: the ladder providers the depth-first dispatcher consumes. Each is a
Expand Down Expand Up @@ -10704,18 +10738,33 @@ export async function main(): Promise<void> {
return null;
}
},
/**
* Run 106 (live finding on the dev replay queue, 2026-10-07): the descriptor read the model and the
* effort off the endpoint's TOP LEVEL, where a registry endpoint candidate does not carry them - the
* identity lives at `identity.model_id` and `identity.reasoning_effort` (see
* `EndpointCandidate` in packages/endpoint-registry). Every descriptor therefore answered
* `modelId: ""` and `reasoningEffort: null`, so the arm comparability the queue handoff records
* was always `arm_effort_unspecified` and the focus-dispatch effort view was empty - which is
* exactly why an effort-mismatched arm could be chosen and then voided downstream. Read the identity
* fields, keeping the top-level shape as a fallback for an endpoint object that carries them.
*/
() =>
created.effectiveRegistry.endpoints.map((endpoint) => ({
endpointId: endpoint.identity.endpoint_id,
modelId:
typeof (endpoint as { modelId?: unknown }).modelId === "string"
? String((endpoint as { modelId?: unknown }).modelId)
: "",
reasoningEffort:
typeof (endpoint as { reasoningEffort?: unknown }).reasoningEffort === "string"
? String((endpoint as { reasoningEffort?: unknown }).reasoningEffort)
: null,
})),
created.effectiveRegistry.endpoints.map((endpoint) => {
const declared = endpoint as {
readonly modelId?: unknown;
readonly reasoningEffort?: unknown;
};
const modelId = endpoint.identity.model_id ?? declared.modelId;
const reasoningEffort = endpoint.identity.reasoning_effort ?? declared.reasoningEffort;
return {
endpointId: endpoint.identity.endpoint_id,
modelId: typeof modelId === "string" ? modelId : "",
reasoningEffort:
typeof reasoningEffort === "string" && reasoningEffort.trim()
? reasoningEffort
: null,
};
}),
// Run 105 bug 3: pass the wired judge resolution down so the dispatcher's focus planner excludes the
// SAME controller judge the supervised replay endpoint will reject (deepseek-flash), instead of
// returning null from the empty durable table and planning the judge as a counterfactual arm.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,18 @@ function replayIdForGroup(group: Row): string | null {
return replayIds.every((id) => id !== null && id === replayIds[0]) ? replayIds[0] : null;
}

/**
* Run 106: the comparability spellings the durable record may carry for one arm's reasoning effort. The
* dimension is recorded, never gating (see the guard below), but an unknown spelling still fails closed so
* this readback stays a faithful projection of what Evaluation Core wrote.
*/
const ARM_EFFORT_COMPARABILITY_VALUES = new Set([
"matched",
"mismatched",
"source_effort_unspecified",
"arm_effort_unspecified",
]);

async function project(
listed: Row,
request: RouteDispatchEvidenceReadRequest,
Expand Down Expand Up @@ -130,9 +142,20 @@ async function project(
return null;
if (actual.outcome !== "source" && actual.outcome !== "candidate" && actual.outcome !== "tie")
return null;
/**
* Run 106 (operator decision, 2026-10-07): the effort dimension is recorded, never gating. A comparison
* whose arms run at different reasoning efforts is valid evidence - a judge grading its own arm or two
* byte-identical outcomes are not, and those keep their fail-closed meaning here. The dimension is still
* required to be present and well formed, so the readback stays a faithful projection of the durable
* record rather than accepting whatever shape arrives.
*/
const COMPARABILITY_ONLY_VALIDITY_ISSUES = new Set(["arm_effort_mismatch"]);
if (
actual.validityIssues !== undefined &&
(!Array.isArray(actual.validityIssues) || actual.validityIssues.length > 0)
(!Array.isArray(actual.validityIssues) ||
actual.validityIssues.some(
(issue) => !COMPARABILITY_ONLY_VALIDITY_ISSUES.has(text(issue) ?? ""),
))
)
return null;
const effort = proof.effortComparability;
Expand All @@ -141,11 +164,9 @@ async function project(
if (
!arm ||
arm.endpointId !== candidate ||
arm.comparability !== "matched" ||
!ARM_EFFORT_COMPARABILITY_VALUES.has(text(arm.comparability) ?? "") ||
!text(arm.modelId) ||
!text(arm.sourceModelId) ||
!text(arm.reasoningEffort) ||
arm.reasoningEffort !== arm.sourceReasoningEffort
!text(arm.sourceModelId)
)
return null;
if (
Expand Down Expand Up @@ -515,6 +536,7 @@ export async function readRouteReplayableCaptures(input: {
readonly scopeId: string;
readonly channel: string;
readonly request: { readonly roleId: string; readonly taskTypeId: string };
/** The capsule read resolves the transcript out of the encrypted artifact store itself. */
readonly readCapture: (requestId: string) => Promise<unknown>;
}): Promise<readonly AutoReplayCapture[] | null> {
if (!text(input.request.roleId) || !text(input.request.taskTypeId) || !text(input.scopeId))
Expand Down Expand Up @@ -568,48 +590,75 @@ export async function readRouteReplayableCaptures(input: {
for (const requestId of ids) {
const capture = row(await input.readCapture(requestId));
if (!capture) continue; // Exact read knows eviction/absence; telemetry alone is not a capsule.
const messages = capture.messages;
const classification = row(capture.classification);
const trace = row(capture.trace);
const source = row(capture.replaySource);
const roleId = text(capture.roleId) ?? text(classification?.roleId);
const taskTypeId = text(capture.taskTypeId) ?? text(classification?.taskTypeId);
const sourceClass = text(row(capture.replayEvidenceClass)?.class);
if (
capture.requestId !== requestId ||
capture.scope !== scope ||
roleId !== input.request.roleId ||
taskTypeId !== input.request.taskTypeId ||
(classification?.roleId != null && classification.roleId !== roleId) ||
(classification?.taskTypeId != null && classification.taskTypeId !== taskTypeId) ||
!text(capture.endpointId) ||
!text(capture.rootArtifactId) ||
!source ||
!trace ||
trace.scopeId !== scope ||
trace.channel !== input.channel ||
trace.readiness !== "ready" ||
trace.completeness !== "complete" ||
!Number.isSafeInteger(trace.generation) ||
Number(trace.generation) < 0 ||
!text(trace.rootOccurrenceId) ||
!text(trace.closedAt) ||
![
/**
* Every gate is named once, in one ordered table, so the corpus read has exactly one
* expression of its admission rule. A rejected capture used to leave the tick with an
* unattributable "no replayable request", which is what made a corpus that persisted and
* hydrated correctly look like it had never been captured. The names are operator-facing:
* with ROLE_MODEL_FOCUS_DIAG set, one tick prints the gate that dropped each capture.
*/
const rejections: string[] = [];
const gate = (name: string, admitted: boolean): void => {
if (!admitted) rejections.push(name);
};
gate("request_id", capture.requestId === requestId);
gate("scope", capture.scope === scope);
gate("role", roleId === input.request.roleId);
gate("task", taskTypeId === input.request.taskTypeId);
gate(
"classification_role",
classification?.roleId == null || classification.roleId === roleId,
);
gate(
"classification_task",
classification?.taskTypeId == null || classification.taskTypeId === taskTypeId,
);
gate("endpoint", text(capture.endpointId) != null);
gate("root_artifact", text(capture.rootArtifactId) != null);
gate("replay_source", source != null);
gate("trace", trace != null);
gate("trace_scope", trace?.scopeId === scope);
gate("trace_channel", trace?.channel === input.channel);
gate("trace_readiness", trace?.readiness === "ready");
gate("trace_completeness", trace?.completeness === "complete");
gate(
"trace_generation",
Number.isSafeInteger(trace?.generation) && Number(trace?.generation) >= 0,
);
gate("trace_root_occurrence", text(trace?.rootOccurrenceId) != null);
gate("trace_closed_at", text(trace?.closedAt) != null);
gate(
"replay_source_refs",
[
"normalizedRequestRef",
"sharedPrefixRef",
"forkOccurrenceId",
"policySnapshotRef",
"capturePolicyRef",
].every((key) => text(source[key])) ||
!Array.isArray(capture.messages) ||
capture.messages.length === 0 ||
capture.branchKind != null ||
capture.branchPhase != null ||
capture.replayProduced === true ||
/^replay-/.test(requestId) ||
isBenchmarkReplaySourceRef(requestId) ||
isSyntheticProbeSourceClass(sourceClass)
)
].every((key) => text(source?.[key]) != null),
);
gate("messages", Array.isArray(messages) && messages.length > 0);
gate("not_branch", capture.branchKind == null && capture.branchPhase == null);
gate("not_replay_produced", capture.replayProduced !== true);
gate("not_replay_request_id", !/^replay-/.test(requestId));
gate("not_benchmark_source", !isBenchmarkReplaySourceRef(requestId));
gate("not_synthetic_probe", !isSyntheticProbeSourceClass(sourceClass));
if (rejections.length > 0) {
if (process.env.ROLE_MODEL_FOCUS_DIAG)
console.error(
`[replay-projection] capture=${requestId} rejected=${rejections.join(",")} trace=${trace ? `${String(trace.scopeId) === scope ? "scope-ok" : "scope-mismatch"}/${String(trace.channel)}/${String(trace.readiness)}/${String(trace.completeness)}` : "absent"} messages=${Array.isArray(messages) ? messages.length : "unreadable"}`,
);
continue;
}
// Narrowing only: the messages gate above already rejected every non-array transcript.
if (!Array.isArray(messages)) continue;
captures.push({
captureRef: requestId,
sourceEndpointId: capture.endpointId as string,
Expand All @@ -618,13 +667,17 @@ export async function readRouteReplayableCaptures(input: {
hasRecordedToolResults: hasRecordedToolResults(capture),
replayProduced: false,
sourceClass,
messages: capture.messages,
messages,
requirements: readReplayRequestRequirements(capture),
replayDispositionSettled: settledRefs.has(requestId),
});
}
return captures;
} catch {
} catch (error) {
if (process.env.ROLE_MODEL_FOCUS_DIAG)
console.error(
`[replay-projection] unavailable: ${error instanceof Error ? error.message : String(error)}`,
);
return null;
}
}
Loading
Loading