diff --git a/packages/openclinxr/graphql/src/documents/faculty-disposition.graphql b/packages/openclinxr/graphql/src/documents/faculty-disposition.graphql new file mode 100644 index 00000000..cf499b1b --- /dev/null +++ b/packages/openclinxr/graphql/src/documents/faculty-disposition.graphql @@ -0,0 +1,61 @@ +fragment FacultyDispositionTrailFields on FacultyDispositionTrail { + examRunId + packetDigest + evidencePacket { + examRunId + packetDigest + learnerId + stationRunIds + claimBoundary + notEvidenceFor + examEquivalenceGate + } + decisions { + decisionId + examRunId + reviewerId + packetDigest + disposition + status + rationale + attestedAt + sequence + } + current { + decisionId + examRunId + reviewerId + packetDigest + disposition + status + rationale + attestedAt + sequence + } + claimBoundary + notEvidenceFor + scoringValidityClaimed + examEquivalenceGate +} + +fragment FacultyDispositionRefusalFields on FacultyDispositionRefusal { + code + reason + notEvidenceFor + scoringValidityClaimed + examEquivalenceGate +} + +query AssembledExamFacultyDisposition($examRunId: ID!) { + assembledExamFacultyDisposition(examRunId: $examRunId) { + ...FacultyDispositionTrailFields + } +} + +mutation AppendAssembledExamFacultyDisposition($input: AppendFacultyDispositionInput!) { + appendAssembledExamFacultyDisposition(input: $input) { + __typename + ...FacultyDispositionTrailFields + ...FacultyDispositionRefusalFields + } +} diff --git a/packages/openclinxr/graphql/src/faculty-disposition.ts b/packages/openclinxr/graphql/src/faculty-disposition.ts new file mode 100644 index 00000000..cea4894f --- /dev/null +++ b/packages/openclinxr/graphql/src/faculty-disposition.ts @@ -0,0 +1,235 @@ +const PRIVATE_KEYS = new Set([ + "hiddenFacts", + "privateFacts", + "hiddenFactRefs", + "serverOnlyNotes", + "hiddenFact", + "confidentialNote", +]); + +export const FACULTY_DISPOSITION_CLAIM_BOUNDARY = + "assembled_exam_faculty_disposition_not_score_use" as const; + +export const FACULTY_DISPOSITION_NOT_EVIDENCE_FOR = [ + "exam_equivalence", + "clinical_validity", + "scoring_validity", + "automated_scoring", + "credentialing", + "production_deployment", +] as const; + +const FACULTY_DISPOSITION_REFUSAL_TYPENAME = { + stale_packet_digest: "FacultyDispositionStaleDigest", + producer_self_review: "FacultyDispositionProducerSelfReview", + identity_mutation: "FacultyDispositionIdentityMutation", + overwrite_refused: "FacultyDispositionOverwriteRefused", + finalized: "FacultyDispositionPostFinalization", +} as const; + +export type FacultyDispositionRefusalCode = keyof typeof FACULTY_DISPOSITION_REFUSAL_TYPENAME; + +export type FacultyDispositionGraphqlRefusal = { + __typename: (typeof FACULTY_DISPOSITION_REFUSAL_TYPENAME)[FacultyDispositionRefusalCode]; + code: FacultyDispositionRefusalCode; + reason: string; + notEvidenceFor: readonly string[]; + scoringValidityClaimed: false; + examEquivalenceGate: false; +}; + +export type FacultyDispositionGraphqlDecision = { + decisionId: string; + examRunId: string; + reviewerId: string; + packetDigest: string; + disposition: string; + status: string; + rationale: string; + attestedAt: string; + sequence: number; +}; + +export type AssembledExamEvidencePacketGraphql = { + examRunId: string; + packetDigest: string; + learnerId?: string | null; + stationRunIds: string[]; + claimBoundary: string; + notEvidenceFor: readonly string[]; + examEquivalenceGate: false; +}; + +export type FacultyDispositionGraphqlTrail = { + __typename: "FacultyDispositionTrail"; + examRunId: string; + packetDigest: string; + evidencePacket: AssembledExamEvidencePacketGraphql; + decisions: FacultyDispositionGraphqlDecision[]; + current: FacultyDispositionGraphqlDecision | null; + claimBoundary: typeof FACULTY_DISPOSITION_CLAIM_BOUNDARY; + notEvidenceFor: typeof FACULTY_DISPOSITION_NOT_EVIDENCE_FOR; + scoringValidityClaimed: false; + examEquivalenceGate: false; +}; + +export function projectFacultyDispositionTrail(source: unknown): FacultyDispositionGraphqlTrail | null { + if (!isRecord(source)) { + return null; + } + const examRunId = nonEmptyString(read(source, "examRunId")); + const packetDigest = nonEmptyString(read(source, "packetDigest")); + const evidencePacket = projectEvidencePacket( + read(source, "evidencePacket"), + examRunId, + packetDigest, + ); + if (!examRunId || !packetDigest || !evidencePacket) { + return null; + } + const decisions = projectDecisions(read(source, "decisions"), examRunId, packetDigest); + return { + __typename: "FacultyDispositionTrail", + examRunId, + packetDigest, + evidencePacket, + decisions, + current: decisions[decisions.length - 1] ?? null, + claimBoundary: FACULTY_DISPOSITION_CLAIM_BOUNDARY, + notEvidenceFor: FACULTY_DISPOSITION_NOT_EVIDENCE_FOR, + scoringValidityClaimed: false, + examEquivalenceGate: false, + }; +} + +export function projectAppendFacultyDispositionResult( + source: unknown, +): FacultyDispositionGraphqlTrail | FacultyDispositionGraphqlRefusal | null { + const refusal = projectFacultyDispositionRefusal(source); + if (refusal) { + return refusal; + } + return projectFacultyDispositionTrail(source); +} + +function projectFacultyDispositionRefusal(source: unknown): FacultyDispositionGraphqlRefusal | undefined { + if (!isRecord(source)) { + return undefined; + } + const codeValue = read(source, "error") ?? read(source, "code"); + if (typeof codeValue !== "string" || !(codeValue in FACULTY_DISPOSITION_REFUSAL_TYPENAME)) { + return undefined; + } + const code = codeValue as FacultyDispositionRefusalCode; + const reasonValue = read(source, "reason"); + return { + __typename: FACULTY_DISPOSITION_REFUSAL_TYPENAME[code], + code, + reason: typeof reasonValue === "string" && reasonValue.length > 0 ? reasonValue : code, + notEvidenceFor: [...FACULTY_DISPOSITION_NOT_EVIDENCE_FOR], + scoringValidityClaimed: false, + examEquivalenceGate: false, + }; +} + +function projectEvidencePacket( + value: unknown, + examRunId: string | undefined, + packetDigest: string | undefined, +): AssembledExamEvidencePacketGraphql | undefined { + const packet = isRecord(value) ? stripPrivate(value) : {}; + const packetExamRunId = nonEmptyString(read(packet, "examRunId")) ?? examRunId; + const digest = packetDigest ?? nonEmptyString(read(packet, "packetDigest")); + if (!packetExamRunId || !digest) { + return undefined; + } + const learnerId = nonEmptyString(read(packet, "learnerId")) ?? null; + const stations = read(packet, "stations"); + const stationRunIds = Array.isArray(stations) + ? stations.flatMap((station) => { + if (!isRecord(station)) { + return []; + } + const reviewPacket = read(station, "reviewPacket"); + const fromReview = isRecord(reviewPacket) ? nonEmptyString(read(reviewPacket, "stationRunId")) : undefined; + const identity = read(station, "identity"); + const fromIdentity = isRecord(identity) ? nonEmptyString(read(identity, "stationRunId")) : undefined; + const id = fromReview ?? fromIdentity ?? nonEmptyString(read(station, "stationRunId")); + return id ? [id] : []; + }) + : stringArray(read(packet, "stationRunIds")); + return { + examRunId: packetExamRunId, + packetDigest: digest, + learnerId, + stationRunIds, + claimBoundary: nonEmptyString(read(packet, "claimBoundary")) + ?? "assembled_exam_review_packet_not_exam_equivalence", + notEvidenceFor: stringArray(read(packet, "notEvidenceFor")), + examEquivalenceGate: false, + }; +} + +function projectDecisions( + value: unknown, + examRunId: string, + packetDigest: string, +): FacultyDispositionGraphqlDecision[] { + if (!Array.isArray(value)) { + return []; + } + return value.flatMap((item, index) => { + if (!isRecord(item)) { + return []; + } + const decisionId = nonEmptyString(read(item, "decisionId")); + const reviewerId = nonEmptyString(read(item, "reviewerId")); + const disposition = nonEmptyString(read(item, "disposition")); + const status = nonEmptyString(read(item, "status")); + const rationale = nonEmptyString(read(item, "rationale")); + const attestedAt = nonEmptyString(read(item, "attestedAt")); + const sequenceValue = read(item, "sequence"); + const sequence = typeof sequenceValue === "number" && Number.isInteger(sequenceValue) ? sequenceValue : index + 1; + if (!decisionId || !reviewerId || !disposition || !status || !rationale || !attestedAt) { + return []; + } + return [{ + decisionId, + examRunId: nonEmptyString(read(item, "examRunId")) ?? examRunId, + reviewerId, + packetDigest: nonEmptyString(read(item, "packetDigest")) ?? packetDigest, + disposition, + status, + rationale, + attestedAt, + sequence, + }]; + }); +} + +function stripPrivate(value: Record): Record { + const next: Record = {}; + for (const [key, entry] of Object.entries(value)) { + if (PRIVATE_KEYS.has(key)) { + continue; + } + next[key] = isRecord(entry) ? stripPrivate(entry) : entry; + } + return next; +} + +function stringArray(value: unknown): string[] { + return Array.isArray(value) ? value.filter((item): item is string => typeof item === "string" && item.length > 0) : []; +} + +function nonEmptyString(value: unknown): string | undefined { + return typeof value === "string" && value.trim().length > 0 ? value : undefined; +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +function read(record: Record, key: string): unknown { + return record[key]; +} diff --git a/packages/openclinxr/graphql/src/generated/client/gql.ts b/packages/openclinxr/graphql/src/generated/client/gql.ts index e418d434..364deac2 100644 --- a/packages/openclinxr/graphql/src/generated/client/gql.ts +++ b/packages/openclinxr/graphql/src/generated/client/gql.ts @@ -17,6 +17,7 @@ type Documents = { "mutation AssembleExamForm($input: AssembleExamFormInput!) {\n assembleExamForm(input: $input) {\n examFormId\n blueprintId\n status\n stationRefs {\n order\n scenarioId\n scenarioVersion\n title\n }\n }\n}": typeof types.AssembleExamFormDocument, "mutation CreateStationRunQueueSnapshot($input: CreateStationRunQueueSnapshotInput!) {\n createStationRunQueueSnapshot(input: $input) {\n snapshotId\n createdAt\n reviewerId\n queue {\n blueprintId\n canStartLearnerExam\n breakCheckpoints {\n afterStationOrder\n atSecond\n }\n totalStationTimeSeconds\n summary {\n activationReady\n draftBlocked\n governanceBlocked\n missingScenario\n }\n stationQueue {\n stationOrder\n slotId\n label\n scenarioId\n scenarioVersion\n status\n blockers\n timing {\n stationOrder\n slotId\n label\n doorway {\n startsAtSecond\n endsAtSecond\n durationSeconds\n }\n encounter {\n startsAtSecond\n endsAtSecond\n durationSeconds\n }\n note {\n startsAtSecond\n endsAtSecond\n durationSeconds\n }\n }\n }\n }\n }\n}": typeof types.CreateStationRunQueueSnapshotDocument, "query ExamFormWorkbench($examFormId: ID!, $scenarioId: ID!, $scenarioVersion: Int!) {\n examForm(examFormId: $examFormId) {\n examFormId\n blueprintId\n status\n stationRefs {\n order\n scenarioId\n scenarioVersion\n title\n }\n coverage\n }\n assetReadiness(scenarioId: $scenarioId, version: $scenarioVersion) {\n devReady\n productionReady\n missingRequiredAssetIds\n productionBlockedAssets {\n assetId\n blockers\n }\n productionReadinessLadder {\n productionReady\n assetCount\n productionReadyAssetIds\n blockedAssetIds\n blockers\n }\n }\n}": typeof types.ExamFormWorkbenchDocument, + "fragment FacultyDispositionTrailFields on FacultyDispositionTrail {\n examRunId\n packetDigest\n evidencePacket {\n examRunId\n packetDigest\n learnerId\n stationRunIds\n claimBoundary\n notEvidenceFor\n examEquivalenceGate\n }\n decisions {\n decisionId\n examRunId\n reviewerId\n packetDigest\n disposition\n status\n rationale\n attestedAt\n sequence\n }\n current {\n decisionId\n examRunId\n reviewerId\n packetDigest\n disposition\n status\n rationale\n attestedAt\n sequence\n }\n claimBoundary\n notEvidenceFor\n scoringValidityClaimed\n examEquivalenceGate\n}\n\nfragment FacultyDispositionRefusalFields on FacultyDispositionRefusal {\n code\n reason\n notEvidenceFor\n scoringValidityClaimed\n examEquivalenceGate\n}\n\nquery AssembledExamFacultyDisposition($examRunId: ID!) {\n assembledExamFacultyDisposition(examRunId: $examRunId) {\n ...FacultyDispositionTrailFields\n }\n}\n\nmutation AppendAssembledExamFacultyDisposition($input: AppendFacultyDispositionInput!) {\n appendAssembledExamFacultyDisposition(input: $input) {\n __typename\n ...FacultyDispositionTrailFields\n ...FacultyDispositionRefusalFields\n }\n}": typeof types.FacultyDispositionTrailFieldsFragmentDoc, "query ReviewPacketReplay($stationRunId: ID!) {\n reviewPacket(stationRunId: $stationRunId) {\n stationRunId\n scenarioId\n observedTraceTags\n missingRequiredTraceTags\n lateTraceTags\n unsafeEvents\n timeline {\n sequence\n atSecond\n eventType\n source\n actorId\n tag\n summary\n }\n traceQuality {\n eventCount\n modelGeneratedEventCount\n modelFailedEventCount\n voiceAudioEventCount\n blockedGuardrailCount\n unsafeEventCount\n missingRequiredTraceTagCount\n hasPatientNote\n hasModelProvenance\n }\n patientNote {\n submittedAtSecond\n text\n }\n facultyScoreDraft {\n reviewerId\n status\n comments\n }\n actorTurns {\n plan {\n planId\n spokenText\n dialogueEmotionFrom\n dialogueEmotionTo\n performancePlanId\n facePresetId\n eventKind\n droppedTags\n claimScope\n }\n execution {\n planId\n truncated\n visemeCueCount\n ttsProviderId\n interruptionKind\n }\n }\n emotionalTimeline {\n turnIndex\n actorId\n from\n to\n trigger\n planId\n atSecond\n }\n prosodyNeutralized\n }\n clinicalEventReviewSummary(stationRunId: $stationRunId) {\n stationRunId\n eventCount\n redactedEventCount\n clinicalEventKinds\n traceTags\n statusCounts\n latestAtSecond\n durableStore\n safeForFacultyReview\n }\n reviewReplayReadinessSummary(stationRunId: $stationRunId) {\n stationRunId\n replayEvidenceReady\n facultyReviewSafe\n timelineEntryCount\n traceEventCount\n durableEventCount\n redactedDurableEventCount\n missingRequiredBehaviorCount\n lateBehaviorCount\n safetySignalCount\n blockers\n recommendedNextAction\n replayBoundary\n runtimeVisualEvidenceReplayProjection {\n schemaVersion\n source\n stationRunId\n scenarioId\n reviewedMetadataOnlyCount\n heldMetadataOnlyCount\n acceptedAttachmentRefCount\n runtimeEvidenceRefCount\n visualQaEvidenceRefCount\n acceptedActionIds\n rawPayloadDisplayed\n providerExecutionAllowed\n runtimeExecutionAllowed\n learnerLaunchAllowed\n questEvidenceRefreshAllowed\n productionAssetReadinessClaimed\n clinicalValidityClaimed\n scoringValidityClaimed\n replayEvidenceReady\n blockerIds\n nextActions\n uiXrConsumerOperatorWorkflowSummary {\n schemaVersion\n source\n scenarioId\n acceptedAttachmentRefCount\n runtimeEvidenceRefCount\n visualQaEvidenceRefCount\n targetRoute\n method\n submitBodyRef\n submitPreview {\n route\n bodyRef\n attachmentCount\n actionIds\n inputIds\n localArtifactPaths\n rawPayloadDisplayed\n claimBoundary\n }\n reviewerAction\n preflightChecks\n nextActions\n rawPayloadDisplayed\n providerExecutionAllowed\n runtimeExecutionAllowed\n learnerLaunchAllowed\n questEvidenceRefreshAllowed\n productionAssetReadinessClaimed\n clinicalValidityClaimed\n scoringValidityClaimed\n blockerIds\n claimBoundary\n notEvidenceFor\n }\n claimBoundary\n notEvidenceFor\n }\n }\n traceEvents(stationRunId: $stationRunId) {\n sequence\n eventType\n atSecond\n source\n actorId\n tag\n }\n}": typeof types.ReviewPacketReplayDocument, "mutation SaveFacultyScoreDraft($input: FacultyScoreDraftInput!) {\n saveFacultyScoreDraft(input: $input) {\n stationRunId\n scenarioId\n observedTraceTags\n missingRequiredTraceTags\n lateTraceTags\n unsafeEvents\n traceQuality {\n eventCount\n modelGeneratedEventCount\n modelFailedEventCount\n voiceAudioEventCount\n blockedGuardrailCount\n unsafeEventCount\n missingRequiredTraceTagCount\n hasPatientNote\n hasModelProvenance\n }\n facultyScoreDraft {\n reviewerId\n status\n comments\n }\n }\n}": typeof types.SaveFacultyScoreDraftDocument, "query ScenarioBank($status: ScenarioStatus) {\n scenarios(status: $status) {\n scenarioId\n version\n title\n status\n clinicalObjectives\n requiredTraceTags\n review {\n clinical\n psychometric\n legal\n simulationQa\n }\n governance {\n scoreUseLabel\n syntheticCaseDisclosure\n validationStage\n requiredReviewerRoles\n sourceIds\n }\n actors {\n actorId\n role\n displayName\n demeanor\n communicationProfile {\n styleFamily\n style\n intensity\n baselineMood\n communicativeness\n }\n }\n assetNeeds {\n assetId\n assetType\n licenseStatus\n }\n }\n}": typeof types.ScenarioBankDocument, @@ -29,6 +30,7 @@ const documents: Documents = { "mutation AssembleExamForm($input: AssembleExamFormInput!) {\n assembleExamForm(input: $input) {\n examFormId\n blueprintId\n status\n stationRefs {\n order\n scenarioId\n scenarioVersion\n title\n }\n }\n}": types.AssembleExamFormDocument, "mutation CreateStationRunQueueSnapshot($input: CreateStationRunQueueSnapshotInput!) {\n createStationRunQueueSnapshot(input: $input) {\n snapshotId\n createdAt\n reviewerId\n queue {\n blueprintId\n canStartLearnerExam\n breakCheckpoints {\n afterStationOrder\n atSecond\n }\n totalStationTimeSeconds\n summary {\n activationReady\n draftBlocked\n governanceBlocked\n missingScenario\n }\n stationQueue {\n stationOrder\n slotId\n label\n scenarioId\n scenarioVersion\n status\n blockers\n timing {\n stationOrder\n slotId\n label\n doorway {\n startsAtSecond\n endsAtSecond\n durationSeconds\n }\n encounter {\n startsAtSecond\n endsAtSecond\n durationSeconds\n }\n note {\n startsAtSecond\n endsAtSecond\n durationSeconds\n }\n }\n }\n }\n }\n}": types.CreateStationRunQueueSnapshotDocument, "query ExamFormWorkbench($examFormId: ID!, $scenarioId: ID!, $scenarioVersion: Int!) {\n examForm(examFormId: $examFormId) {\n examFormId\n blueprintId\n status\n stationRefs {\n order\n scenarioId\n scenarioVersion\n title\n }\n coverage\n }\n assetReadiness(scenarioId: $scenarioId, version: $scenarioVersion) {\n devReady\n productionReady\n missingRequiredAssetIds\n productionBlockedAssets {\n assetId\n blockers\n }\n productionReadinessLadder {\n productionReady\n assetCount\n productionReadyAssetIds\n blockedAssetIds\n blockers\n }\n }\n}": types.ExamFormWorkbenchDocument, + "fragment FacultyDispositionTrailFields on FacultyDispositionTrail {\n examRunId\n packetDigest\n evidencePacket {\n examRunId\n packetDigest\n learnerId\n stationRunIds\n claimBoundary\n notEvidenceFor\n examEquivalenceGate\n }\n decisions {\n decisionId\n examRunId\n reviewerId\n packetDigest\n disposition\n status\n rationale\n attestedAt\n sequence\n }\n current {\n decisionId\n examRunId\n reviewerId\n packetDigest\n disposition\n status\n rationale\n attestedAt\n sequence\n }\n claimBoundary\n notEvidenceFor\n scoringValidityClaimed\n examEquivalenceGate\n}\n\nfragment FacultyDispositionRefusalFields on FacultyDispositionRefusal {\n code\n reason\n notEvidenceFor\n scoringValidityClaimed\n examEquivalenceGate\n}\n\nquery AssembledExamFacultyDisposition($examRunId: ID!) {\n assembledExamFacultyDisposition(examRunId: $examRunId) {\n ...FacultyDispositionTrailFields\n }\n}\n\nmutation AppendAssembledExamFacultyDisposition($input: AppendFacultyDispositionInput!) {\n appendAssembledExamFacultyDisposition(input: $input) {\n __typename\n ...FacultyDispositionTrailFields\n ...FacultyDispositionRefusalFields\n }\n}": types.FacultyDispositionTrailFieldsFragmentDoc, "query ReviewPacketReplay($stationRunId: ID!) {\n reviewPacket(stationRunId: $stationRunId) {\n stationRunId\n scenarioId\n observedTraceTags\n missingRequiredTraceTags\n lateTraceTags\n unsafeEvents\n timeline {\n sequence\n atSecond\n eventType\n source\n actorId\n tag\n summary\n }\n traceQuality {\n eventCount\n modelGeneratedEventCount\n modelFailedEventCount\n voiceAudioEventCount\n blockedGuardrailCount\n unsafeEventCount\n missingRequiredTraceTagCount\n hasPatientNote\n hasModelProvenance\n }\n patientNote {\n submittedAtSecond\n text\n }\n facultyScoreDraft {\n reviewerId\n status\n comments\n }\n actorTurns {\n plan {\n planId\n spokenText\n dialogueEmotionFrom\n dialogueEmotionTo\n performancePlanId\n facePresetId\n eventKind\n droppedTags\n claimScope\n }\n execution {\n planId\n truncated\n visemeCueCount\n ttsProviderId\n interruptionKind\n }\n }\n emotionalTimeline {\n turnIndex\n actorId\n from\n to\n trigger\n planId\n atSecond\n }\n prosodyNeutralized\n }\n clinicalEventReviewSummary(stationRunId: $stationRunId) {\n stationRunId\n eventCount\n redactedEventCount\n clinicalEventKinds\n traceTags\n statusCounts\n latestAtSecond\n durableStore\n safeForFacultyReview\n }\n reviewReplayReadinessSummary(stationRunId: $stationRunId) {\n stationRunId\n replayEvidenceReady\n facultyReviewSafe\n timelineEntryCount\n traceEventCount\n durableEventCount\n redactedDurableEventCount\n missingRequiredBehaviorCount\n lateBehaviorCount\n safetySignalCount\n blockers\n recommendedNextAction\n replayBoundary\n runtimeVisualEvidenceReplayProjection {\n schemaVersion\n source\n stationRunId\n scenarioId\n reviewedMetadataOnlyCount\n heldMetadataOnlyCount\n acceptedAttachmentRefCount\n runtimeEvidenceRefCount\n visualQaEvidenceRefCount\n acceptedActionIds\n rawPayloadDisplayed\n providerExecutionAllowed\n runtimeExecutionAllowed\n learnerLaunchAllowed\n questEvidenceRefreshAllowed\n productionAssetReadinessClaimed\n clinicalValidityClaimed\n scoringValidityClaimed\n replayEvidenceReady\n blockerIds\n nextActions\n uiXrConsumerOperatorWorkflowSummary {\n schemaVersion\n source\n scenarioId\n acceptedAttachmentRefCount\n runtimeEvidenceRefCount\n visualQaEvidenceRefCount\n targetRoute\n method\n submitBodyRef\n submitPreview {\n route\n bodyRef\n attachmentCount\n actionIds\n inputIds\n localArtifactPaths\n rawPayloadDisplayed\n claimBoundary\n }\n reviewerAction\n preflightChecks\n nextActions\n rawPayloadDisplayed\n providerExecutionAllowed\n runtimeExecutionAllowed\n learnerLaunchAllowed\n questEvidenceRefreshAllowed\n productionAssetReadinessClaimed\n clinicalValidityClaimed\n scoringValidityClaimed\n blockerIds\n claimBoundary\n notEvidenceFor\n }\n claimBoundary\n notEvidenceFor\n }\n }\n traceEvents(stationRunId: $stationRunId) {\n sequence\n eventType\n atSecond\n source\n actorId\n tag\n }\n}": types.ReviewPacketReplayDocument, "mutation SaveFacultyScoreDraft($input: FacultyScoreDraftInput!) {\n saveFacultyScoreDraft(input: $input) {\n stationRunId\n scenarioId\n observedTraceTags\n missingRequiredTraceTags\n lateTraceTags\n unsafeEvents\n traceQuality {\n eventCount\n modelGeneratedEventCount\n modelFailedEventCount\n voiceAudioEventCount\n blockedGuardrailCount\n unsafeEventCount\n missingRequiredTraceTagCount\n hasPatientNote\n hasModelProvenance\n }\n facultyScoreDraft {\n reviewerId\n status\n comments\n }\n }\n}": types.SaveFacultyScoreDraftDocument, "query ScenarioBank($status: ScenarioStatus) {\n scenarios(status: $status) {\n scenarioId\n version\n title\n status\n clinicalObjectives\n requiredTraceTags\n review {\n clinical\n psychometric\n legal\n simulationQa\n }\n governance {\n scoreUseLabel\n syntheticCaseDisclosure\n validationStage\n requiredReviewerRoles\n sourceIds\n }\n actors {\n actorId\n role\n displayName\n demeanor\n communicationProfile {\n styleFamily\n style\n intensity\n baselineMood\n communicativeness\n }\n }\n assetNeeds {\n assetId\n assetType\n licenseStatus\n }\n }\n}": types.ScenarioBankDocument, @@ -64,6 +66,10 @@ export function graphql(source: "mutation CreateStationRunQueueSnapshot($input: * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "query ExamFormWorkbench($examFormId: ID!, $scenarioId: ID!, $scenarioVersion: Int!) {\n examForm(examFormId: $examFormId) {\n examFormId\n blueprintId\n status\n stationRefs {\n order\n scenarioId\n scenarioVersion\n title\n }\n coverage\n }\n assetReadiness(scenarioId: $scenarioId, version: $scenarioVersion) {\n devReady\n productionReady\n missingRequiredAssetIds\n productionBlockedAssets {\n assetId\n blockers\n }\n productionReadinessLadder {\n productionReady\n assetCount\n productionReadyAssetIds\n blockedAssetIds\n blockers\n }\n }\n}"): (typeof documents)["query ExamFormWorkbench($examFormId: ID!, $scenarioId: ID!, $scenarioVersion: Int!) {\n examForm(examFormId: $examFormId) {\n examFormId\n blueprintId\n status\n stationRefs {\n order\n scenarioId\n scenarioVersion\n title\n }\n coverage\n }\n assetReadiness(scenarioId: $scenarioId, version: $scenarioVersion) {\n devReady\n productionReady\n missingRequiredAssetIds\n productionBlockedAssets {\n assetId\n blockers\n }\n productionReadinessLadder {\n productionReady\n assetCount\n productionReadyAssetIds\n blockedAssetIds\n blockers\n }\n }\n}"]; +/** + * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. + */ +export function graphql(source: "fragment FacultyDispositionTrailFields on FacultyDispositionTrail {\n examRunId\n packetDigest\n evidencePacket {\n examRunId\n packetDigest\n learnerId\n stationRunIds\n claimBoundary\n notEvidenceFor\n examEquivalenceGate\n }\n decisions {\n decisionId\n examRunId\n reviewerId\n packetDigest\n disposition\n status\n rationale\n attestedAt\n sequence\n }\n current {\n decisionId\n examRunId\n reviewerId\n packetDigest\n disposition\n status\n rationale\n attestedAt\n sequence\n }\n claimBoundary\n notEvidenceFor\n scoringValidityClaimed\n examEquivalenceGate\n}\n\nfragment FacultyDispositionRefusalFields on FacultyDispositionRefusal {\n code\n reason\n notEvidenceFor\n scoringValidityClaimed\n examEquivalenceGate\n}\n\nquery AssembledExamFacultyDisposition($examRunId: ID!) {\n assembledExamFacultyDisposition(examRunId: $examRunId) {\n ...FacultyDispositionTrailFields\n }\n}\n\nmutation AppendAssembledExamFacultyDisposition($input: AppendFacultyDispositionInput!) {\n appendAssembledExamFacultyDisposition(input: $input) {\n __typename\n ...FacultyDispositionTrailFields\n ...FacultyDispositionRefusalFields\n }\n}"): (typeof documents)["fragment FacultyDispositionTrailFields on FacultyDispositionTrail {\n examRunId\n packetDigest\n evidencePacket {\n examRunId\n packetDigest\n learnerId\n stationRunIds\n claimBoundary\n notEvidenceFor\n examEquivalenceGate\n }\n decisions {\n decisionId\n examRunId\n reviewerId\n packetDigest\n disposition\n status\n rationale\n attestedAt\n sequence\n }\n current {\n decisionId\n examRunId\n reviewerId\n packetDigest\n disposition\n status\n rationale\n attestedAt\n sequence\n }\n claimBoundary\n notEvidenceFor\n scoringValidityClaimed\n examEquivalenceGate\n}\n\nfragment FacultyDispositionRefusalFields on FacultyDispositionRefusal {\n code\n reason\n notEvidenceFor\n scoringValidityClaimed\n examEquivalenceGate\n}\n\nquery AssembledExamFacultyDisposition($examRunId: ID!) {\n assembledExamFacultyDisposition(examRunId: $examRunId) {\n ...FacultyDispositionTrailFields\n }\n}\n\nmutation AppendAssembledExamFacultyDisposition($input: AppendFacultyDispositionInput!) {\n appendAssembledExamFacultyDisposition(input: $input) {\n __typename\n ...FacultyDispositionTrailFields\n ...FacultyDispositionRefusalFields\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/openclinxr/graphql/src/generated/client/graphql.ts b/packages/openclinxr/graphql/src/generated/client/graphql.ts index 37c2c12d..64b4aa10 100644 --- a/packages/openclinxr/graphql/src/generated/client/graphql.ts +++ b/packages/openclinxr/graphql/src/generated/client/graphql.ts @@ -4,6 +4,20 @@ type Exact = { [K in keyof T]: T[K] }; /** Internal type. DO NOT USE DIRECTLY. */ export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; +export type AppendFacultyDispositionInput = { + attestedAt: string; + decisionId?: string | number | null | undefined; + decisions?: unknown; + disposition: FacultyDispositionValue; + evidencePacket?: unknown; + examRunId: string | number; + learnerId?: string | number | null | undefined; + packetDigest: string; + rationale: string; + reviewerId: string | number; + status: FacultyDispositionStatus; +}; + export type AssembleExamFormInput = { blueprintId?: string | number | null | undefined; examFormId: string | number; @@ -16,6 +30,22 @@ export type CreateStationRunQueueSnapshotInput = { snapshotId?: string | number | null | undefined; }; +export type FacultyDispositionRefusalCode = + | 'finalized' + | 'identity_mutation' + | 'overwrite_refused' + | 'producer_self_review' + | 'stale_packet_digest'; + +export type FacultyDispositionStatus = + | 'draft' + | 'final'; + +export type FacultyDispositionValue = + | 'hold' + | 'local_debrief_ready' + | 'needs_revision'; + export type FacultyScoreDraftInput = { comments: string; reviewerId: string | number; @@ -87,6 +117,65 @@ export type ExamFormWorkbenchQueryVariables = Exact<{ export type ExamFormWorkbenchQuery = { examForm: { examFormId: string, blueprintId: string, status: string, coverage: unknown, stationRefs: Array<{ order: number, scenarioId: string, scenarioVersion: number, title: string }> } | null, assetReadiness: { devReady: boolean, productionReady: boolean, missingRequiredAssetIds: Array, productionBlockedAssets: Array<{ assetId: string, blockers: Array }>, productionReadinessLadder: { productionReady: boolean, assetCount: number, productionReadyAssetIds: Array, blockedAssetIds: Array, blockers: Array } } }; +export type FacultyDispositionTrailFieldsFragment = { examRunId: string, packetDigest: string, claimBoundary: string, notEvidenceFor: Array, scoringValidityClaimed: boolean, examEquivalenceGate: boolean, evidencePacket: { examRunId: string, packetDigest: string, learnerId: string | null, stationRunIds: Array, claimBoundary: string, notEvidenceFor: Array, examEquivalenceGate: boolean }, decisions: Array<{ decisionId: string, examRunId: string, reviewerId: string, packetDigest: string, disposition: FacultyDispositionValue, status: FacultyDispositionStatus, rationale: string, attestedAt: string, sequence: number }>, current: { decisionId: string, examRunId: string, reviewerId: string, packetDigest: string, disposition: FacultyDispositionValue, status: FacultyDispositionStatus, rationale: string, attestedAt: string, sequence: number } | null } & { ' $fragmentName'?: 'FacultyDispositionTrailFieldsFragment' }; + +type FacultyDispositionRefusalFields_FacultyDispositionIdentityMutation_Fragment = { code: FacultyDispositionRefusalCode, reason: string, notEvidenceFor: Array, scoringValidityClaimed: boolean, examEquivalenceGate: boolean } & { ' $fragmentName'?: 'FacultyDispositionRefusalFields_FacultyDispositionIdentityMutation_Fragment' }; + +type FacultyDispositionRefusalFields_FacultyDispositionOverwriteRefused_Fragment = { code: FacultyDispositionRefusalCode, reason: string, notEvidenceFor: Array, scoringValidityClaimed: boolean, examEquivalenceGate: boolean } & { ' $fragmentName'?: 'FacultyDispositionRefusalFields_FacultyDispositionOverwriteRefused_Fragment' }; + +type FacultyDispositionRefusalFields_FacultyDispositionPostFinalization_Fragment = { code: FacultyDispositionRefusalCode, reason: string, notEvidenceFor: Array, scoringValidityClaimed: boolean, examEquivalenceGate: boolean } & { ' $fragmentName'?: 'FacultyDispositionRefusalFields_FacultyDispositionPostFinalization_Fragment' }; + +type FacultyDispositionRefusalFields_FacultyDispositionProducerSelfReview_Fragment = { code: FacultyDispositionRefusalCode, reason: string, notEvidenceFor: Array, scoringValidityClaimed: boolean, examEquivalenceGate: boolean } & { ' $fragmentName'?: 'FacultyDispositionRefusalFields_FacultyDispositionProducerSelfReview_Fragment' }; + +type FacultyDispositionRefusalFields_FacultyDispositionStaleDigest_Fragment = { code: FacultyDispositionRefusalCode, reason: string, notEvidenceFor: Array, scoringValidityClaimed: boolean, examEquivalenceGate: boolean } & { ' $fragmentName'?: 'FacultyDispositionRefusalFields_FacultyDispositionStaleDigest_Fragment' }; + +export type FacultyDispositionRefusalFieldsFragment = + | FacultyDispositionRefusalFields_FacultyDispositionIdentityMutation_Fragment + | FacultyDispositionRefusalFields_FacultyDispositionOverwriteRefused_Fragment + | FacultyDispositionRefusalFields_FacultyDispositionPostFinalization_Fragment + | FacultyDispositionRefusalFields_FacultyDispositionProducerSelfReview_Fragment + | FacultyDispositionRefusalFields_FacultyDispositionStaleDigest_Fragment +; + +export type AssembledExamFacultyDispositionQueryVariables = Exact<{ + examRunId: string | number; +}>; + + +export type AssembledExamFacultyDispositionQuery = { assembledExamFacultyDisposition: { ' $fragmentRefs'?: { 'FacultyDispositionTrailFieldsFragment': FacultyDispositionTrailFieldsFragment } } | null }; + +export type AppendAssembledExamFacultyDispositionMutationVariables = Exact<{ + input: AppendFacultyDispositionInput; +}>; + + +export type AppendAssembledExamFacultyDispositionMutation = { appendAssembledExamFacultyDisposition: + | ( + { __typename: 'FacultyDispositionIdentityMutation' } + & { ' $fragmentRefs'?: { 'FacultyDispositionRefusalFields_FacultyDispositionIdentityMutation_Fragment': FacultyDispositionRefusalFields_FacultyDispositionIdentityMutation_Fragment } } + ) + | ( + { __typename: 'FacultyDispositionOverwriteRefused' } + & { ' $fragmentRefs'?: { 'FacultyDispositionRefusalFields_FacultyDispositionOverwriteRefused_Fragment': FacultyDispositionRefusalFields_FacultyDispositionOverwriteRefused_Fragment } } + ) + | ( + { __typename: 'FacultyDispositionPostFinalization' } + & { ' $fragmentRefs'?: { 'FacultyDispositionRefusalFields_FacultyDispositionPostFinalization_Fragment': FacultyDispositionRefusalFields_FacultyDispositionPostFinalization_Fragment } } + ) + | ( + { __typename: 'FacultyDispositionProducerSelfReview' } + & { ' $fragmentRefs'?: { 'FacultyDispositionRefusalFields_FacultyDispositionProducerSelfReview_Fragment': FacultyDispositionRefusalFields_FacultyDispositionProducerSelfReview_Fragment } } + ) + | ( + { __typename: 'FacultyDispositionStaleDigest' } + & { ' $fragmentRefs'?: { 'FacultyDispositionRefusalFields_FacultyDispositionStaleDigest_Fragment': FacultyDispositionRefusalFields_FacultyDispositionStaleDigest_Fragment } } + ) + | ( + { __typename: 'FacultyDispositionTrail' } + & { ' $fragmentRefs'?: { 'FacultyDispositionTrailFieldsFragment': FacultyDispositionTrailFieldsFragment } } + ) + }; + export type ReviewPacketReplayQueryVariables = Exact<{ stationRunId: string | number; }>; @@ -138,10 +227,13 @@ export type SubmitScenarioReviewMutationVariables = Exact<{ export type SubmitScenarioReviewMutation = { submitScenarioReview: { scenarioId: string, version: number, title: string, status: ScenarioStatus, clinicalObjectives: Array, requiredTraceTags: Array, equipment: Array, review: { clinical: string, psychometric: string, legal: string, simulationQa: string }, governance: { scoreUseLabel: string, syntheticCaseDisclosure: string, validationStage: string, requiredReviewerRoles: Array, sourceIds: Array }, environment: { environmentId: string, name: string, description: string } | null, actors: Array<{ actorId: string, role: string, displayName: string, demeanor: string | null, communicationProfile: { styleFamily: string, style: string, intensity: number, baselineMood: Array, communicativeness: string, topicsToAvoid: Array, adverseResponse: string, deescalationTriggers: Array, escalationTriggers: Array, culturalLanguageNotes: Array } | null }>, assetNeeds: Array<{ assetId: string, assetType: string, description: string, licenseStatus: string }> } }; - +export const FacultyDispositionTrailFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FacultyDispositionTrailFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FacultyDispositionTrail"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"evidencePacket"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"learnerId"}},{"kind":"Field","name":{"kind":"Name","value":"stationRunIds"}},{"kind":"Field","name":{"kind":"Name","value":"claimBoundary"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}},{"kind":"Field","name":{"kind":"Name","value":"examEquivalenceGate"}}]}},{"kind":"Field","name":{"kind":"Name","value":"decisions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"decisionId"}},{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"reviewerId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"disposition"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"rationale"}},{"kind":"Field","name":{"kind":"Name","value":"attestedAt"}},{"kind":"Field","name":{"kind":"Name","value":"sequence"}}]}},{"kind":"Field","name":{"kind":"Name","value":"current"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"decisionId"}},{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"reviewerId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"disposition"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"rationale"}},{"kind":"Field","name":{"kind":"Name","value":"attestedAt"}},{"kind":"Field","name":{"kind":"Name","value":"sequence"}}]}},{"kind":"Field","name":{"kind":"Name","value":"claimBoundary"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}},{"kind":"Field","name":{"kind":"Name","value":"scoringValidityClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"examEquivalenceGate"}}]}}]} as unknown as DocumentNode; +export const FacultyDispositionRefusalFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FacultyDispositionRefusalFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FacultyDispositionRefusal"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}},{"kind":"Field","name":{"kind":"Name","value":"scoringValidityClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"examEquivalenceGate"}}]}}]} as unknown as DocumentNode; export const AssembleExamFormDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AssembleExamForm"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AssembleExamFormInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assembleExamForm"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"examFormId"}},{"kind":"Field","name":{"kind":"Name","value":"blueprintId"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"stationRefs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioId"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioVersion"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateStationRunQueueSnapshotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateStationRunQueueSnapshot"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateStationRunQueueSnapshotInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createStationRunQueueSnapshot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"snapshotId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"reviewerId"}},{"kind":"Field","name":{"kind":"Name","value":"queue"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blueprintId"}},{"kind":"Field","name":{"kind":"Name","value":"canStartLearnerExam"}},{"kind":"Field","name":{"kind":"Name","value":"breakCheckpoints"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"afterStationOrder"}},{"kind":"Field","name":{"kind":"Name","value":"atSecond"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalStationTimeSeconds"}},{"kind":"Field","name":{"kind":"Name","value":"summary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activationReady"}},{"kind":"Field","name":{"kind":"Name","value":"draftBlocked"}},{"kind":"Field","name":{"kind":"Name","value":"governanceBlocked"}},{"kind":"Field","name":{"kind":"Name","value":"missingScenario"}}]}},{"kind":"Field","name":{"kind":"Name","value":"stationQueue"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stationOrder"}},{"kind":"Field","name":{"kind":"Name","value":"slotId"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioId"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioVersion"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"blockers"}},{"kind":"Field","name":{"kind":"Name","value":"timing"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stationOrder"}},{"kind":"Field","name":{"kind":"Name","value":"slotId"}},{"kind":"Field","name":{"kind":"Name","value":"label"}},{"kind":"Field","name":{"kind":"Name","value":"doorway"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startsAtSecond"}},{"kind":"Field","name":{"kind":"Name","value":"endsAtSecond"}},{"kind":"Field","name":{"kind":"Name","value":"durationSeconds"}}]}},{"kind":"Field","name":{"kind":"Name","value":"encounter"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startsAtSecond"}},{"kind":"Field","name":{"kind":"Name","value":"endsAtSecond"}},{"kind":"Field","name":{"kind":"Name","value":"durationSeconds"}}]}},{"kind":"Field","name":{"kind":"Name","value":"note"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"startsAtSecond"}},{"kind":"Field","name":{"kind":"Name","value":"endsAtSecond"}},{"kind":"Field","name":{"kind":"Name","value":"durationSeconds"}}]}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const ExamFormWorkbenchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ExamFormWorkbench"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"examFormId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"scenarioId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"scenarioVersion"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"examForm"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"examFormId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"examFormId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"examFormId"}},{"kind":"Field","name":{"kind":"Name","value":"blueprintId"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"stationRefs"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioId"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioVersion"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}},{"kind":"Field","name":{"kind":"Name","value":"coverage"}}]}},{"kind":"Field","name":{"kind":"Name","value":"assetReadiness"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"scenarioId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"scenarioId"}}},{"kind":"Argument","name":{"kind":"Name","value":"version"},"value":{"kind":"Variable","name":{"kind":"Name","value":"scenarioVersion"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"devReady"}},{"kind":"Field","name":{"kind":"Name","value":"productionReady"}},{"kind":"Field","name":{"kind":"Name","value":"missingRequiredAssetIds"}},{"kind":"Field","name":{"kind":"Name","value":"productionBlockedAssets"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assetId"}},{"kind":"Field","name":{"kind":"Name","value":"blockers"}}]}},{"kind":"Field","name":{"kind":"Name","value":"productionReadinessLadder"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"productionReady"}},{"kind":"Field","name":{"kind":"Name","value":"assetCount"}},{"kind":"Field","name":{"kind":"Name","value":"productionReadyAssetIds"}},{"kind":"Field","name":{"kind":"Name","value":"blockedAssetIds"}},{"kind":"Field","name":{"kind":"Name","value":"blockers"}}]}}]}}]}}]} as unknown as DocumentNode; +export const AssembledExamFacultyDispositionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AssembledExamFacultyDisposition"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"examRunId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assembledExamFacultyDisposition"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"examRunId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"examRunId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"FacultyDispositionTrailFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FacultyDispositionTrailFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FacultyDispositionTrail"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"evidencePacket"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"learnerId"}},{"kind":"Field","name":{"kind":"Name","value":"stationRunIds"}},{"kind":"Field","name":{"kind":"Name","value":"claimBoundary"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}},{"kind":"Field","name":{"kind":"Name","value":"examEquivalenceGate"}}]}},{"kind":"Field","name":{"kind":"Name","value":"decisions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"decisionId"}},{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"reviewerId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"disposition"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"rationale"}},{"kind":"Field","name":{"kind":"Name","value":"attestedAt"}},{"kind":"Field","name":{"kind":"Name","value":"sequence"}}]}},{"kind":"Field","name":{"kind":"Name","value":"current"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"decisionId"}},{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"reviewerId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"disposition"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"rationale"}},{"kind":"Field","name":{"kind":"Name","value":"attestedAt"}},{"kind":"Field","name":{"kind":"Name","value":"sequence"}}]}},{"kind":"Field","name":{"kind":"Name","value":"claimBoundary"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}},{"kind":"Field","name":{"kind":"Name","value":"scoringValidityClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"examEquivalenceGate"}}]}}]} as unknown as DocumentNode; +export const AppendAssembledExamFacultyDispositionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"AppendAssembledExamFacultyDisposition"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"AppendFacultyDispositionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"appendAssembledExamFacultyDisposition"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"__typename"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FacultyDispositionTrailFields"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FacultyDispositionRefusalFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FacultyDispositionTrailFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FacultyDispositionTrail"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"evidencePacket"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"learnerId"}},{"kind":"Field","name":{"kind":"Name","value":"stationRunIds"}},{"kind":"Field","name":{"kind":"Name","value":"claimBoundary"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}},{"kind":"Field","name":{"kind":"Name","value":"examEquivalenceGate"}}]}},{"kind":"Field","name":{"kind":"Name","value":"decisions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"decisionId"}},{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"reviewerId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"disposition"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"rationale"}},{"kind":"Field","name":{"kind":"Name","value":"attestedAt"}},{"kind":"Field","name":{"kind":"Name","value":"sequence"}}]}},{"kind":"Field","name":{"kind":"Name","value":"current"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"decisionId"}},{"kind":"Field","name":{"kind":"Name","value":"examRunId"}},{"kind":"Field","name":{"kind":"Name","value":"reviewerId"}},{"kind":"Field","name":{"kind":"Name","value":"packetDigest"}},{"kind":"Field","name":{"kind":"Name","value":"disposition"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"rationale"}},{"kind":"Field","name":{"kind":"Name","value":"attestedAt"}},{"kind":"Field","name":{"kind":"Name","value":"sequence"}}]}},{"kind":"Field","name":{"kind":"Name","value":"claimBoundary"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}},{"kind":"Field","name":{"kind":"Name","value":"scoringValidityClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"examEquivalenceGate"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FacultyDispositionRefusalFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FacultyDispositionRefusal"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"reason"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}},{"kind":"Field","name":{"kind":"Name","value":"scoringValidityClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"examEquivalenceGate"}}]}}]} as unknown as DocumentNode; export const ReviewPacketReplayDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ReviewPacketReplay"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"stationRunId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewPacket"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"stationRunId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stationRunId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stationRunId"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioId"}},{"kind":"Field","name":{"kind":"Name","value":"observedTraceTags"}},{"kind":"Field","name":{"kind":"Name","value":"missingRequiredTraceTags"}},{"kind":"Field","name":{"kind":"Name","value":"lateTraceTags"}},{"kind":"Field","name":{"kind":"Name","value":"unsafeEvents"}},{"kind":"Field","name":{"kind":"Name","value":"timeline"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sequence"}},{"kind":"Field","name":{"kind":"Name","value":"atSecond"}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"actorId"}},{"kind":"Field","name":{"kind":"Name","value":"tag"}},{"kind":"Field","name":{"kind":"Name","value":"summary"}}]}},{"kind":"Field","name":{"kind":"Name","value":"traceQuality"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"eventCount"}},{"kind":"Field","name":{"kind":"Name","value":"modelGeneratedEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"modelFailedEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"voiceAudioEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"blockedGuardrailCount"}},{"kind":"Field","name":{"kind":"Name","value":"unsafeEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"missingRequiredTraceTagCount"}},{"kind":"Field","name":{"kind":"Name","value":"hasPatientNote"}},{"kind":"Field","name":{"kind":"Name","value":"hasModelProvenance"}}]}},{"kind":"Field","name":{"kind":"Name","value":"patientNote"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"submittedAtSecond"}},{"kind":"Field","name":{"kind":"Name","value":"text"}}]}},{"kind":"Field","name":{"kind":"Name","value":"facultyScoreDraft"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewerId"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"comments"}}]}},{"kind":"Field","name":{"kind":"Name","value":"actorTurns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"planId"}},{"kind":"Field","name":{"kind":"Name","value":"spokenText"}},{"kind":"Field","name":{"kind":"Name","value":"dialogueEmotionFrom"}},{"kind":"Field","name":{"kind":"Name","value":"dialogueEmotionTo"}},{"kind":"Field","name":{"kind":"Name","value":"performancePlanId"}},{"kind":"Field","name":{"kind":"Name","value":"facePresetId"}},{"kind":"Field","name":{"kind":"Name","value":"eventKind"}},{"kind":"Field","name":{"kind":"Name","value":"droppedTags"}},{"kind":"Field","name":{"kind":"Name","value":"claimScope"}}]}},{"kind":"Field","name":{"kind":"Name","value":"execution"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"planId"}},{"kind":"Field","name":{"kind":"Name","value":"truncated"}},{"kind":"Field","name":{"kind":"Name","value":"visemeCueCount"}},{"kind":"Field","name":{"kind":"Name","value":"ttsProviderId"}},{"kind":"Field","name":{"kind":"Name","value":"interruptionKind"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"emotionalTimeline"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"turnIndex"}},{"kind":"Field","name":{"kind":"Name","value":"actorId"}},{"kind":"Field","name":{"kind":"Name","value":"from"}},{"kind":"Field","name":{"kind":"Name","value":"to"}},{"kind":"Field","name":{"kind":"Name","value":"trigger"}},{"kind":"Field","name":{"kind":"Name","value":"planId"}},{"kind":"Field","name":{"kind":"Name","value":"atSecond"}}]}},{"kind":"Field","name":{"kind":"Name","value":"prosodyNeutralized"}}]}},{"kind":"Field","name":{"kind":"Name","value":"clinicalEventReviewSummary"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"stationRunId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stationRunId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stationRunId"}},{"kind":"Field","name":{"kind":"Name","value":"eventCount"}},{"kind":"Field","name":{"kind":"Name","value":"redactedEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"clinicalEventKinds"}},{"kind":"Field","name":{"kind":"Name","value":"traceTags"}},{"kind":"Field","name":{"kind":"Name","value":"statusCounts"}},{"kind":"Field","name":{"kind":"Name","value":"latestAtSecond"}},{"kind":"Field","name":{"kind":"Name","value":"durableStore"}},{"kind":"Field","name":{"kind":"Name","value":"safeForFacultyReview"}}]}},{"kind":"Field","name":{"kind":"Name","value":"reviewReplayReadinessSummary"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"stationRunId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stationRunId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stationRunId"}},{"kind":"Field","name":{"kind":"Name","value":"replayEvidenceReady"}},{"kind":"Field","name":{"kind":"Name","value":"facultyReviewSafe"}},{"kind":"Field","name":{"kind":"Name","value":"timelineEntryCount"}},{"kind":"Field","name":{"kind":"Name","value":"traceEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"durableEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"redactedDurableEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"missingRequiredBehaviorCount"}},{"kind":"Field","name":{"kind":"Name","value":"lateBehaviorCount"}},{"kind":"Field","name":{"kind":"Name","value":"safetySignalCount"}},{"kind":"Field","name":{"kind":"Name","value":"blockers"}},{"kind":"Field","name":{"kind":"Name","value":"recommendedNextAction"}},{"kind":"Field","name":{"kind":"Name","value":"replayBoundary"}},{"kind":"Field","name":{"kind":"Name","value":"runtimeVisualEvidenceReplayProjection"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"schemaVersion"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"stationRunId"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioId"}},{"kind":"Field","name":{"kind":"Name","value":"reviewedMetadataOnlyCount"}},{"kind":"Field","name":{"kind":"Name","value":"heldMetadataOnlyCount"}},{"kind":"Field","name":{"kind":"Name","value":"acceptedAttachmentRefCount"}},{"kind":"Field","name":{"kind":"Name","value":"runtimeEvidenceRefCount"}},{"kind":"Field","name":{"kind":"Name","value":"visualQaEvidenceRefCount"}},{"kind":"Field","name":{"kind":"Name","value":"acceptedActionIds"}},{"kind":"Field","name":{"kind":"Name","value":"rawPayloadDisplayed"}},{"kind":"Field","name":{"kind":"Name","value":"providerExecutionAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"runtimeExecutionAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"learnerLaunchAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"questEvidenceRefreshAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"productionAssetReadinessClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"clinicalValidityClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"scoringValidityClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"replayEvidenceReady"}},{"kind":"Field","name":{"kind":"Name","value":"blockerIds"}},{"kind":"Field","name":{"kind":"Name","value":"nextActions"}},{"kind":"Field","name":{"kind":"Name","value":"uiXrConsumerOperatorWorkflowSummary"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"schemaVersion"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioId"}},{"kind":"Field","name":{"kind":"Name","value":"acceptedAttachmentRefCount"}},{"kind":"Field","name":{"kind":"Name","value":"runtimeEvidenceRefCount"}},{"kind":"Field","name":{"kind":"Name","value":"visualQaEvidenceRefCount"}},{"kind":"Field","name":{"kind":"Name","value":"targetRoute"}},{"kind":"Field","name":{"kind":"Name","value":"method"}},{"kind":"Field","name":{"kind":"Name","value":"submitBodyRef"}},{"kind":"Field","name":{"kind":"Name","value":"submitPreview"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"route"}},{"kind":"Field","name":{"kind":"Name","value":"bodyRef"}},{"kind":"Field","name":{"kind":"Name","value":"attachmentCount"}},{"kind":"Field","name":{"kind":"Name","value":"actionIds"}},{"kind":"Field","name":{"kind":"Name","value":"inputIds"}},{"kind":"Field","name":{"kind":"Name","value":"localArtifactPaths"}},{"kind":"Field","name":{"kind":"Name","value":"rawPayloadDisplayed"}},{"kind":"Field","name":{"kind":"Name","value":"claimBoundary"}}]}},{"kind":"Field","name":{"kind":"Name","value":"reviewerAction"}},{"kind":"Field","name":{"kind":"Name","value":"preflightChecks"}},{"kind":"Field","name":{"kind":"Name","value":"nextActions"}},{"kind":"Field","name":{"kind":"Name","value":"rawPayloadDisplayed"}},{"kind":"Field","name":{"kind":"Name","value":"providerExecutionAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"runtimeExecutionAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"learnerLaunchAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"questEvidenceRefreshAllowed"}},{"kind":"Field","name":{"kind":"Name","value":"productionAssetReadinessClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"clinicalValidityClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"scoringValidityClaimed"}},{"kind":"Field","name":{"kind":"Name","value":"blockerIds"}},{"kind":"Field","name":{"kind":"Name","value":"claimBoundary"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}}]}},{"kind":"Field","name":{"kind":"Name","value":"claimBoundary"}},{"kind":"Field","name":{"kind":"Name","value":"notEvidenceFor"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"traceEvents"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"stationRunId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"stationRunId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"sequence"}},{"kind":"Field","name":{"kind":"Name","value":"eventType"}},{"kind":"Field","name":{"kind":"Name","value":"atSecond"}},{"kind":"Field","name":{"kind":"Name","value":"source"}},{"kind":"Field","name":{"kind":"Name","value":"actorId"}},{"kind":"Field","name":{"kind":"Name","value":"tag"}}]}}]}}]} as unknown as DocumentNode; export const SaveFacultyScoreDraftDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SaveFacultyScoreDraft"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"FacultyScoreDraftInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"saveFacultyScoreDraft"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stationRunId"}},{"kind":"Field","name":{"kind":"Name","value":"scenarioId"}},{"kind":"Field","name":{"kind":"Name","value":"observedTraceTags"}},{"kind":"Field","name":{"kind":"Name","value":"missingRequiredTraceTags"}},{"kind":"Field","name":{"kind":"Name","value":"lateTraceTags"}},{"kind":"Field","name":{"kind":"Name","value":"unsafeEvents"}},{"kind":"Field","name":{"kind":"Name","value":"traceQuality"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"eventCount"}},{"kind":"Field","name":{"kind":"Name","value":"modelGeneratedEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"modelFailedEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"voiceAudioEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"blockedGuardrailCount"}},{"kind":"Field","name":{"kind":"Name","value":"unsafeEventCount"}},{"kind":"Field","name":{"kind":"Name","value":"missingRequiredTraceTagCount"}},{"kind":"Field","name":{"kind":"Name","value":"hasPatientNote"}},{"kind":"Field","name":{"kind":"Name","value":"hasModelProvenance"}}]}},{"kind":"Field","name":{"kind":"Name","value":"facultyScoreDraft"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"reviewerId"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"comments"}}]}}]}}]}}]} as unknown as DocumentNode; export const ScenarioBankDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ScenarioBank"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"status"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ScenarioStatus"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scenarios"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"status"},"value":{"kind":"Variable","name":{"kind":"Name","value":"status"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scenarioId"}},{"kind":"Field","name":{"kind":"Name","value":"version"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"clinicalObjectives"}},{"kind":"Field","name":{"kind":"Name","value":"requiredTraceTags"}},{"kind":"Field","name":{"kind":"Name","value":"review"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"clinical"}},{"kind":"Field","name":{"kind":"Name","value":"psychometric"}},{"kind":"Field","name":{"kind":"Name","value":"legal"}},{"kind":"Field","name":{"kind":"Name","value":"simulationQa"}}]}},{"kind":"Field","name":{"kind":"Name","value":"governance"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scoreUseLabel"}},{"kind":"Field","name":{"kind":"Name","value":"syntheticCaseDisclosure"}},{"kind":"Field","name":{"kind":"Name","value":"validationStage"}},{"kind":"Field","name":{"kind":"Name","value":"requiredReviewerRoles"}},{"kind":"Field","name":{"kind":"Name","value":"sourceIds"}}]}},{"kind":"Field","name":{"kind":"Name","value":"actors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"actorId"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"demeanor"}},{"kind":"Field","name":{"kind":"Name","value":"communicationProfile"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"styleFamily"}},{"kind":"Field","name":{"kind":"Name","value":"style"}},{"kind":"Field","name":{"kind":"Name","value":"intensity"}},{"kind":"Field","name":{"kind":"Name","value":"baselineMood"}},{"kind":"Field","name":{"kind":"Name","value":"communicativeness"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"assetNeeds"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"assetId"}},{"kind":"Field","name":{"kind":"Name","value":"assetType"}},{"kind":"Field","name":{"kind":"Name","value":"licenseStatus"}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/openclinxr/graphql/src/generated/resolvers.generated.ts b/packages/openclinxr/graphql/src/generated/resolvers.generated.ts index 999c4d38..95fe5771 100644 --- a/packages/openclinxr/graphql/src/generated/resolvers.generated.ts +++ b/packages/openclinxr/graphql/src/generated/resolvers.generated.ts @@ -21,12 +21,39 @@ export type Actor = { role: Scalars['String']['output']; }; +export type AppendFacultyDispositionInput = { + attestedAt: Scalars['String']['input']; + decisionId?: InputMaybe; + decisions?: InputMaybe; + disposition: FacultyDispositionValue; + evidencePacket?: InputMaybe; + examRunId: Scalars['ID']['input']; + learnerId?: InputMaybe; + packetDigest: Scalars['String']['input']; + rationale: Scalars['String']['input']; + reviewerId: Scalars['ID']['input']; + status: FacultyDispositionStatus; +}; + +export type AppendFacultyDispositionResult = FacultyDispositionIdentityMutation | FacultyDispositionOverwriteRefused | FacultyDispositionPostFinalization | FacultyDispositionProducerSelfReview | FacultyDispositionStaleDigest | FacultyDispositionTrail; + export type AssembleExamFormInput = { blueprintId?: InputMaybe; examFormId: Scalars['ID']['input']; scenarioIds: Array; }; +export type AssembledExamEvidencePacket = { + __typename?: 'AssembledExamEvidencePacket'; + claimBoundary: Scalars['String']['output']; + examEquivalenceGate: Scalars['Boolean']['output']; + examRunId: Scalars['ID']['output']; + learnerId?: Maybe; + notEvidenceFor: Array; + packetDigest: Scalars['String']['output']; + stationRunIds: Array; +}; + export type AssetBlocker = { __typename?: 'AssetBlocker'; assetId: Scalars['ID']['output']; @@ -134,6 +161,104 @@ export type ExamTimingWindow = { startsAtSecond: Scalars['Int']['output']; }; +export type FacultyDispositionDecision = { + __typename?: 'FacultyDispositionDecision'; + attestedAt: Scalars['String']['output']; + decisionId: Scalars['ID']['output']; + disposition: FacultyDispositionValue; + examRunId: Scalars['ID']['output']; + packetDigest: Scalars['String']['output']; + rationale: Scalars['String']['output']; + reviewerId: Scalars['ID']['output']; + sequence: Scalars['Int']['output']; + status: FacultyDispositionStatus; +}; + +export type FacultyDispositionIdentityMutation = FacultyDispositionRefusal & { + __typename?: 'FacultyDispositionIdentityMutation'; + code: FacultyDispositionRefusalCode; + examEquivalenceGate: Scalars['Boolean']['output']; + notEvidenceFor: Array; + reason: Scalars['String']['output']; + scoringValidityClaimed: Scalars['Boolean']['output']; +}; + +export type FacultyDispositionOverwriteRefused = FacultyDispositionRefusal & { + __typename?: 'FacultyDispositionOverwriteRefused'; + code: FacultyDispositionRefusalCode; + examEquivalenceGate: Scalars['Boolean']['output']; + notEvidenceFor: Array; + reason: Scalars['String']['output']; + scoringValidityClaimed: Scalars['Boolean']['output']; +}; + +export type FacultyDispositionPostFinalization = FacultyDispositionRefusal & { + __typename?: 'FacultyDispositionPostFinalization'; + code: FacultyDispositionRefusalCode; + examEquivalenceGate: Scalars['Boolean']['output']; + notEvidenceFor: Array; + reason: Scalars['String']['output']; + scoringValidityClaimed: Scalars['Boolean']['output']; +}; + +export type FacultyDispositionProducerSelfReview = FacultyDispositionRefusal & { + __typename?: 'FacultyDispositionProducerSelfReview'; + code: FacultyDispositionRefusalCode; + examEquivalenceGate: Scalars['Boolean']['output']; + notEvidenceFor: Array; + reason: Scalars['String']['output']; + scoringValidityClaimed: Scalars['Boolean']['output']; +}; + +export type FacultyDispositionRefusal = { + code: FacultyDispositionRefusalCode; + examEquivalenceGate: Scalars['Boolean']['output']; + notEvidenceFor: Array; + reason: Scalars['String']['output']; + scoringValidityClaimed: Scalars['Boolean']['output']; +}; + +export enum FacultyDispositionRefusalCode { + Finalized = 'finalized', + IdentityMutation = 'identity_mutation', + OverwriteRefused = 'overwrite_refused', + ProducerSelfReview = 'producer_self_review', + StalePacketDigest = 'stale_packet_digest' +} + +export type FacultyDispositionStaleDigest = FacultyDispositionRefusal & { + __typename?: 'FacultyDispositionStaleDigest'; + code: FacultyDispositionRefusalCode; + examEquivalenceGate: Scalars['Boolean']['output']; + notEvidenceFor: Array; + reason: Scalars['String']['output']; + scoringValidityClaimed: Scalars['Boolean']['output']; +}; + +export enum FacultyDispositionStatus { + Draft = 'draft', + Final = 'final' +} + +export type FacultyDispositionTrail = { + __typename?: 'FacultyDispositionTrail'; + claimBoundary: Scalars['String']['output']; + current?: Maybe; + decisions: Array; + evidencePacket: AssembledExamEvidencePacket; + examEquivalenceGate: Scalars['Boolean']['output']; + examRunId: Scalars['ID']['output']; + notEvidenceFor: Array; + packetDigest: Scalars['String']['output']; + scoringValidityClaimed: Scalars['Boolean']['output']; +}; + +export enum FacultyDispositionValue { + Hold = 'hold', + LocalDebriefReady = 'local_debrief_ready', + NeedsRevision = 'needs_revision' +} + export type FacultyScoreDraft = { __typename?: 'FacultyScoreDraft'; comments: Scalars['String']['output']; @@ -156,6 +281,7 @@ export type HiddenFactPolicy = { export type Mutation = { __typename?: 'Mutation'; + appendAssembledExamFacultyDisposition: AppendFacultyDispositionResult; assembleExamForm: ExamForm; createStationRunQueueSnapshot: StationRunQueueSnapshot; saveFacultyScoreDraft: ReviewPacket; @@ -163,6 +289,11 @@ export type Mutation = { }; +export type MutationAppendAssembledExamFacultyDispositionArgs = { + input: AppendFacultyDispositionInput; +}; + + export type MutationAssembleExamFormArgs = { input: AssembleExamFormInput; }; @@ -191,6 +322,7 @@ export type PatientNote = { export type Query = { __typename?: 'Query'; + assembledExamFacultyDisposition?: Maybe; assetReadiness: AssetReadiness; clinicalEventReviewSummary: DurableClinicalEventReviewSummary; examForm?: Maybe; @@ -204,6 +336,11 @@ export type Query = { }; +export type QueryAssembledExamFacultyDispositionArgs = { + examRunId: Scalars['ID']['input']; +}; + + export type QueryAssetReadinessArgs = { scenarioId: Scalars['ID']['input']; version: Scalars['Int']['input']; @@ -680,12 +817,36 @@ export type DirectiveResolverFn, TParent = +/** Mapping of union types */ +export type ResolversUnionTypes<_RefType extends Record> = { + AppendFacultyDispositionResult: + | ( FacultyDispositionIdentityMutation ) + | ( FacultyDispositionOverwriteRefused ) + | ( FacultyDispositionPostFinalization ) + | ( FacultyDispositionProducerSelfReview ) + | ( FacultyDispositionStaleDigest ) + | ( FacultyDispositionTrail ) + ; +}; +/** Mapping of interface types */ +export type ResolversInterfaceTypes<_RefType extends Record> = { + FacultyDispositionRefusal: + | ( FacultyDispositionIdentityMutation ) + | ( FacultyDispositionOverwriteRefused ) + | ( FacultyDispositionPostFinalization ) + | ( FacultyDispositionProducerSelfReview ) + | ( FacultyDispositionStaleDigest ) + ; +}; /** Mapping between all available schema types and the resolvers types */ export type ResolversTypes = { Actor: ResolverTypeWrapper; + AppendFacultyDispositionInput: AppendFacultyDispositionInput; + AppendFacultyDispositionResult: ResolverTypeWrapper['AppendFacultyDispositionResult']>; AssembleExamFormInput: AssembleExamFormInput; + AssembledExamEvidencePacket: ResolverTypeWrapper; AssetBlocker: ResolverTypeWrapper; AssetNeed: ResolverTypeWrapper; AssetProductionReadinessLadder: ResolverTypeWrapper; @@ -699,6 +860,17 @@ export type ResolversTypes = { ExamForm: ResolverTypeWrapper; ExamStationTimingWindow: ResolverTypeWrapper; ExamTimingWindow: ResolverTypeWrapper; + FacultyDispositionDecision: ResolverTypeWrapper; + FacultyDispositionIdentityMutation: ResolverTypeWrapper; + FacultyDispositionOverwriteRefused: ResolverTypeWrapper; + FacultyDispositionPostFinalization: ResolverTypeWrapper; + FacultyDispositionProducerSelfReview: ResolverTypeWrapper; + FacultyDispositionRefusal: ResolverTypeWrapper['FacultyDispositionRefusal']>; + FacultyDispositionRefusalCode: FacultyDispositionRefusalCode; + FacultyDispositionStaleDigest: ResolverTypeWrapper; + FacultyDispositionStatus: FacultyDispositionStatus; + FacultyDispositionTrail: ResolverTypeWrapper; + FacultyDispositionValue: FacultyDispositionValue; FacultyScoreDraft: ResolverTypeWrapper; FacultyScoreDraftInput: FacultyScoreDraftInput; Float: ResolverTypeWrapper; @@ -745,7 +917,10 @@ export type ResolversTypes = { /** Mapping between all available schema types and the resolvers parents */ export type ResolversParentTypes = { Actor: Actor; + AppendFacultyDispositionInput: AppendFacultyDispositionInput; + AppendFacultyDispositionResult: ResolversUnionTypes['AppendFacultyDispositionResult']; AssembleExamFormInput: AssembleExamFormInput; + AssembledExamEvidencePacket: AssembledExamEvidencePacket; AssetBlocker: AssetBlocker; AssetNeed: AssetNeed; AssetProductionReadinessLadder: AssetProductionReadinessLadder; @@ -759,6 +934,14 @@ export type ResolversParentTypes = { ExamForm: ExamForm; ExamStationTimingWindow: ExamStationTimingWindow; ExamTimingWindow: ExamTimingWindow; + FacultyDispositionDecision: FacultyDispositionDecision; + FacultyDispositionIdentityMutation: FacultyDispositionIdentityMutation; + FacultyDispositionOverwriteRefused: FacultyDispositionOverwriteRefused; + FacultyDispositionPostFinalization: FacultyDispositionPostFinalization; + FacultyDispositionProducerSelfReview: FacultyDispositionProducerSelfReview; + FacultyDispositionRefusal: ResolversInterfaceTypes['FacultyDispositionRefusal']; + FacultyDispositionStaleDigest: FacultyDispositionStaleDigest; + FacultyDispositionTrail: FacultyDispositionTrail; FacultyScoreDraft: FacultyScoreDraft; FacultyScoreDraftInput: FacultyScoreDraftInput; Float: Scalars['Float']['output']; @@ -805,6 +988,20 @@ export type ActorResolvers; }; +export type AppendFacultyDispositionResultResolvers = { + __resolveType: TypeResolveFn<'FacultyDispositionIdentityMutation' | 'FacultyDispositionOverwriteRefused' | 'FacultyDispositionPostFinalization' | 'FacultyDispositionProducerSelfReview' | 'FacultyDispositionStaleDigest' | 'FacultyDispositionTrail', ParentType, ContextType>; +}; + +export type AssembledExamEvidencePacketResolvers = { + claimBoundary?: Resolver; + examEquivalenceGate?: Resolver; + examRunId?: Resolver; + learnerId?: Resolver, ParentType, ContextType>; + notEvidenceFor?: Resolver, ParentType, ContextType>; + packetDigest?: Resolver; + stationRunIds?: Resolver, ParentType, ContextType>; +}; + export type AssetBlockerResolvers = { assetId?: Resolver; blockers?: Resolver, ParentType, ContextType>; @@ -895,6 +1092,80 @@ export type ExamTimingWindowResolvers; }; +export type FacultyDispositionDecisionResolvers = { + attestedAt?: Resolver; + decisionId?: Resolver; + disposition?: Resolver; + examRunId?: Resolver; + packetDigest?: Resolver; + rationale?: Resolver; + reviewerId?: Resolver; + sequence?: Resolver; + status?: Resolver; +}; + +export type FacultyDispositionIdentityMutationResolvers = { + code?: Resolver; + examEquivalenceGate?: Resolver; + notEvidenceFor?: Resolver, ParentType, ContextType>; + reason?: Resolver; + scoringValidityClaimed?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type FacultyDispositionOverwriteRefusedResolvers = { + code?: Resolver; + examEquivalenceGate?: Resolver; + notEvidenceFor?: Resolver, ParentType, ContextType>; + reason?: Resolver; + scoringValidityClaimed?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type FacultyDispositionPostFinalizationResolvers = { + code?: Resolver; + examEquivalenceGate?: Resolver; + notEvidenceFor?: Resolver, ParentType, ContextType>; + reason?: Resolver; + scoringValidityClaimed?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type FacultyDispositionProducerSelfReviewResolvers = { + code?: Resolver; + examEquivalenceGate?: Resolver; + notEvidenceFor?: Resolver, ParentType, ContextType>; + reason?: Resolver; + scoringValidityClaimed?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type FacultyDispositionRefusalResolvers = { + __resolveType: TypeResolveFn<'FacultyDispositionIdentityMutation' | 'FacultyDispositionOverwriteRefused' | 'FacultyDispositionPostFinalization' | 'FacultyDispositionProducerSelfReview' | 'FacultyDispositionStaleDigest', ParentType, ContextType>; +}; + +export type FacultyDispositionStaleDigestResolvers = { + code?: Resolver; + examEquivalenceGate?: Resolver; + notEvidenceFor?: Resolver, ParentType, ContextType>; + reason?: Resolver; + scoringValidityClaimed?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type FacultyDispositionTrailResolvers = { + claimBoundary?: Resolver; + current?: Resolver, ParentType, ContextType>; + decisions?: Resolver, ParentType, ContextType>; + evidencePacket?: Resolver; + examEquivalenceGate?: Resolver; + examRunId?: Resolver; + notEvidenceFor?: Resolver, ParentType, ContextType>; + packetDigest?: Resolver; + scoringValidityClaimed?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type FacultyScoreDraftResolvers = { comments?: Resolver; reviewerId?: Resolver; @@ -911,6 +1182,7 @@ export interface JsonScalarConfig extends GraphQLScalarTypeConfig = { + appendAssembledExamFacultyDisposition?: Resolver>; assembleExamForm?: Resolver>; createStationRunQueueSnapshot?: Resolver>; saveFacultyScoreDraft?: Resolver>; @@ -924,6 +1196,7 @@ export type PatientNoteResolvers = { + assembledExamFacultyDisposition?: Resolver, ParentType, ContextType, RequireFields>; assetReadiness?: Resolver>; clinicalEventReviewSummary?: Resolver>; examForm?: Resolver, ParentType, ContextType, RequireFields>; @@ -1218,6 +1491,8 @@ export type UiXrConsumerWorkflowSubmitPreviewResolvers = { Actor?: ActorResolvers; + AppendFacultyDispositionResult?: AppendFacultyDispositionResultResolvers; + AssembledExamEvidencePacket?: AssembledExamEvidencePacketResolvers; AssetBlocker?: AssetBlockerResolvers; AssetNeed?: AssetNeedResolvers; AssetProductionReadinessLadder?: AssetProductionReadinessLadderResolvers; @@ -1229,6 +1504,14 @@ export type Resolvers = { ExamForm?: ExamFormResolvers; ExamStationTimingWindow?: ExamStationTimingWindowResolvers; ExamTimingWindow?: ExamTimingWindowResolvers; + FacultyDispositionDecision?: FacultyDispositionDecisionResolvers; + FacultyDispositionIdentityMutation?: FacultyDispositionIdentityMutationResolvers; + FacultyDispositionOverwriteRefused?: FacultyDispositionOverwriteRefusedResolvers; + FacultyDispositionPostFinalization?: FacultyDispositionPostFinalizationResolvers; + FacultyDispositionProducerSelfReview?: FacultyDispositionProducerSelfReviewResolvers; + FacultyDispositionRefusal?: FacultyDispositionRefusalResolvers; + FacultyDispositionStaleDigest?: FacultyDispositionStaleDigestResolvers; + FacultyDispositionTrail?: FacultyDispositionTrailResolvers; FacultyScoreDraft?: FacultyScoreDraftResolvers; HiddenFactPolicy?: HiddenFactPolicyResolvers; JSON?: GraphQLScalarType; diff --git a/packages/openclinxr/graphql/src/generated/schema.generated.ts b/packages/openclinxr/graphql/src/generated/schema.generated.ts index 04d82358..2f126fcf 100644 --- a/packages/openclinxr/graphql/src/generated/schema.generated.ts +++ b/packages/openclinxr/graphql/src/generated/schema.generated.ts @@ -1,4 +1,4 @@ // This file is auto-generated by packages/openclinxr/graphql/scripts/generate-static-artifacts.ts. // Do not edit directly. -export const openClinXrAdminSchemaSdl = "scalar JSON\n\nenum ScenarioStatus {\n DRAFT\n READY_FOR_REVIEW\n APPROVED\n ARCHIVED\n}\n\nenum ReviewDecision {\n APPROVED\n CHANGES_REQUESTED\n}\n\ntype Query {\n scenario(scenarioId: ID!, version: Int): Scenario\n scenarios(status: ScenarioStatus): [Scenario!]!\n scenarioReviewDecisions(scenarioId: ID!, version: Int!): [ScenarioReviewDecisionRecord!]!\n examForm(examFormId: ID!): ExamForm\n stationRunQueueSnapshots(blueprintId: ID!): [StationRunQueueSnapshot!]!\n reviewPacket(stationRunId: ID!): ReviewPacket\n clinicalEventReviewSummary(stationRunId: ID!): DurableClinicalEventReviewSummary!\n reviewReplayReadinessSummary(stationRunId: ID!): ReviewReplayReadinessSummary!\n traceEvents(stationRunId: ID!): [TraceEvent!]!\n assetReadiness(scenarioId: ID!, version: Int!): AssetReadiness!\n}\n\ntype Mutation {\n assembleExamForm(input: AssembleExamFormInput!): ExamForm!\n createStationRunQueueSnapshot(input: CreateStationRunQueueSnapshotInput!): StationRunQueueSnapshot!\n submitScenarioReview(input: ScenarioReviewDecisionInput!): Scenario!\n saveFacultyScoreDraft(input: FacultyScoreDraftInput!): ReviewPacket!\n}\n\ntype Scenario {\n scenarioId: ID!\n version: Int!\n title: String!\n status: ScenarioStatus!\n clinicalObjectives: [String!]!\n actors: [Actor!]!\n requiredTraceTags: [String!]!\n review: ScenarioReviewState!\n governance: ScenarioGovernance!\n environment: ScenarioEnvironment\n equipment: [String!]!\n assetNeeds: [AssetNeed!]!\n}\n\ntype Actor {\n actorId: ID!\n role: String!\n displayName: String!\n demeanor: String\n communicationProfile: CommunicationProfile\n}\n\ntype CommunicationProfile {\n styleFamily: String!\n style: String!\n intensity: Float!\n baselineMood: [String!]!\n communicativeness: String!\n topicsToAvoid: [String!]!\n adverseResponse: String!\n deescalationTriggers: [String!]!\n escalationTriggers: [String!]!\n culturalLanguageNotes: [String!]!\n}\n\ntype ScenarioReviewState {\n clinical: String!\n psychometric: String!\n legal: String!\n simulationQa: String!\n}\n\ntype ScenarioReviewDecisionRecord {\n scenarioId: ID!\n version: Int!\n reviewerRole: String!\n reviewerId: ID!\n decision: String!\n comments: String!\n evidenceRefs: [String!]!\n reviewedAt: String!\n}\n\ntype ScenarioGovernance {\n scoreUseLabel: String!\n syntheticCaseDisclosure: String!\n validationStage: String!\n validationLimitations: [String!]!\n requiredReviewerRoles: [String!]!\n sourceIds: [String!]!\n safetyCriticalTraceTags: [String!]!\n hiddenFactPolicy: HiddenFactPolicy!\n}\n\ntype HiddenFactPolicy {\n learnerView: String!\n disclosureRequiresTrigger: Boolean!\n}\n\ntype ScenarioEnvironment {\n environmentId: ID!\n name: String!\n description: String!\n}\n\ntype AssetNeed {\n assetId: ID!\n assetType: String!\n description: String!\n licenseStatus: String!\n}\n\ntype AssetReadiness {\n scenarioId: ID!\n devReady: Boolean!\n productionReady: Boolean!\n missingRequiredAssetIds: [ID!]!\n blockedAssets: [AssetBlocker!]!\n productionBlockedAssets: [AssetBlocker!]!\n productionReadinessLadder: ScenarioAssetProductionReadinessLadder!\n}\n\ntype AssetBlocker {\n assetId: ID!\n blockers: [String!]!\n}\n\ntype ScenarioAssetProductionReadinessLadder {\n scenarioId: ID!\n productionReady: Boolean!\n assetCount: Int!\n productionReadyAssetIds: [ID!]!\n blockedAssetIds: [ID!]!\n missingRequiredAssetIds: [ID!]!\n blockers: [String!]!\n stationBudget: ScenarioAssetBudget!\n assetLadders: [AssetProductionReadinessLadder!]!\n}\n\ntype ScenarioAssetBudget {\n maxVisibleTriangles: Int!\n maxTextureMegabytes: Int!\n maxDrawCalls: Int!\n totalTriangles: Int!\n totalTextureMegabytes: Int!\n totalDrawCalls: Int!\n blockers: [String!]!\n}\n\ntype AssetProductionReadinessLadder {\n assetId: ID!\n scenarioId: ID!\n productionReady: Boolean!\n blockers: [String!]!\n steps: [AssetProductionReadinessStep!]!\n}\n\ntype AssetProductionReadinessStep {\n step: String!\n status: String!\n evidenceRefs: [String!]!\n blockers: [String!]!\n}\n\ntype ExamForm {\n examFormId: ID!\n blueprintId: ID!\n status: String!\n stationRefs: [StationRef!]!\n coverage: JSON!\n}\n\ntype StationRef {\n order: Int!\n scenarioId: ID!\n scenarioVersion: Int!\n title: String!\n}\n\ntype StationRunQueueSnapshot {\n snapshotId: ID!\n createdAt: String!\n reviewerId: ID\n queue: StationRunQueue!\n}\n\ntype StationRunQueue {\n blueprintId: ID!\n canStartLearnerExam: Boolean!\n stationQueue: [StationRunQueueItem!]!\n breakCheckpoints: [ExamBreakCheckpoint!]!\n totalStationTimeSeconds: Int!\n summary: StationRunQueueSummary!\n}\n\ntype StationRunQueueItem {\n stationOrder: Int!\n slotId: ID!\n label: String!\n scenarioId: ID\n scenarioVersion: Int\n status: String!\n blockers: [String!]!\n timing: ExamStationTimingWindow!\n}\n\ntype ExamStationTimingWindow {\n stationOrder: Int!\n slotId: ID!\n label: String!\n doorway: ExamTimingWindow!\n encounter: ExamTimingWindow!\n note: ExamTimingWindow!\n}\n\ntype ExamTimingWindow {\n startsAtSecond: Int!\n endsAtSecond: Int!\n durationSeconds: Int!\n}\n\ntype ExamBreakCheckpoint {\n afterStationOrder: Int!\n atSecond: Int!\n}\n\ntype StationRunQueueSummary {\n activationReady: Int!\n draftBlocked: Int!\n governanceBlocked: Int!\n missingScenario: Int!\n}\n\nenum ReviewPacketDialogueEmotion {\n anxious\n concerned\n reassured\n neutral\n}\n\nenum ReviewPacketEmotionEventKind {\n learner_empathetic\n learner_dismissive\n learner_interruption\n actor_silence_timeout\n learner_acknowledgement\n learner_clinical_question\n learner_personal_question\n learner_unclassified\n}\n\nenum ReviewPacketInterruptionKind {\n none\n truncated\n replaced\n}\n\ntype ReviewPacketActorTurnPlan {\n planId: ID!\n spokenText: String!\n dialogueEmotionFrom: ReviewPacketDialogueEmotion!\n dialogueEmotionTo: ReviewPacketDialogueEmotion!\n performancePlanId: ID!\n facePresetId: ID!\n eventKind: ReviewPacketEmotionEventKind!\n droppedTags: [String!]!\n claimScope: String!\n}\n\ntype ReviewPacketActorTurnExecution {\n planId: ID!\n truncated: Boolean!\n visemeCueCount: Int!\n ttsProviderId: String!\n interruptionKind: ReviewPacketInterruptionKind!\n}\n\ntype ReviewPacketActorTurn {\n plan: ReviewPacketActorTurnPlan!\n execution: ReviewPacketActorTurnExecution!\n}\n\ntype ReviewPacketEmotionalTimelineEntry {\n turnIndex: Int\n actorId: ID\n from: String!\n to: String!\n trigger: String\n planId: ID\n atSecond: Int\n}\n\ntype ReviewPacket {\n stationRunId: ID!\n scenarioId: ID!\n observedTraceTags: [String!]!\n missingRequiredTraceTags: [String!]!\n lateTraceTags: [String!]!\n unsafeEvents: [String!]!\n timeline: [TraceTimelineEntry!]!\n traceQuality: ReviewTraceQuality!\n patientNote: PatientNote\n facultyScoreDraft: FacultyScoreDraft!\n actorTurns: [ReviewPacketActorTurn!]!\n emotionalTimeline: [ReviewPacketEmotionalTimelineEntry!]!\n prosodyNeutralized: Boolean!\n}\n\ntype DurableClinicalEventReviewSummary {\n stationRunId: ID\n eventCount: Int!\n redactedEventCount: Int!\n clinicalEventKinds: JSON!\n traceTags: [String!]!\n statusCounts: JSON!\n latestAtSecond: Int\n durableStore: String\n safeForFacultyReview: Boolean!\n}\n\ntype ReviewReplayReadinessSummary {\n stationRunId: ID\n replayEvidenceReady: Boolean!\n facultyReviewSafe: Boolean!\n timelineEntryCount: Int!\n traceEventCount: Int!\n durableEventCount: Int!\n redactedDurableEventCount: Int!\n missingRequiredBehaviorCount: Int!\n lateBehaviorCount: Int!\n safetySignalCount: Int!\n blockers: [String!]!\n recommendedNextAction: String!\n replayBoundary: String!\n runtimeVisualEvidenceReplayProjection: RuntimeVisualEvidenceReplayProjection\n}\n\ntype RuntimeVisualEvidenceReplayProjection {\n schemaVersion: String!\n source: String!\n stationRunId: ID!\n scenarioId: ID!\n reviewedMetadataOnlyCount: Int!\n heldMetadataOnlyCount: Int!\n acceptedAttachmentRefCount: Int!\n runtimeEvidenceRefCount: Int!\n visualQaEvidenceRefCount: Int!\n acceptedActionIds: [String!]!\n rawPayloadDisplayed: Boolean!\n providerExecutionAllowed: Boolean!\n runtimeExecutionAllowed: Boolean!\n learnerLaunchAllowed: Boolean!\n questEvidenceRefreshAllowed: Boolean!\n productionAssetReadinessClaimed: Boolean!\n clinicalValidityClaimed: Boolean!\n scoringValidityClaimed: Boolean!\n replayEvidenceReady: Boolean!\n blockerIds: [String!]!\n nextActions: [String!]!\n uiXrConsumerOperatorWorkflowSummary: UiXrConsumerOperatorWorkflowSummary\n claimBoundary: String!\n notEvidenceFor: [String!]!\n}\n\ntype UiXrConsumerOperatorWorkflowSummary {\n schemaVersion: String!\n source: String!\n scenarioId: ID!\n acceptedAttachmentRefCount: Int!\n runtimeEvidenceRefCount: Int!\n visualQaEvidenceRefCount: Int!\n targetRoute: String!\n method: String!\n submitBodyRef: String!\n submitPreview: UiXrConsumerWorkflowSubmitPreview!\n reviewerAction: String!\n preflightChecks: [String!]!\n nextActions: [String!]!\n rawPayloadDisplayed: Boolean!\n providerExecutionAllowed: Boolean!\n runtimeExecutionAllowed: Boolean!\n learnerLaunchAllowed: Boolean!\n questEvidenceRefreshAllowed: Boolean!\n productionAssetReadinessClaimed: Boolean!\n clinicalValidityClaimed: Boolean!\n scoringValidityClaimed: Boolean!\n blockerIds: [String!]!\n claimBoundary: String!\n notEvidenceFor: [String!]!\n}\n\ntype UiXrConsumerWorkflowSubmitPreview {\n route: String!\n bodyRef: String!\n attachmentCount: Int!\n actionIds: [String!]!\n inputIds: [String!]!\n localArtifactPaths: [String!]!\n rawPayloadDisplayed: Boolean!\n claimBoundary: String!\n}\n\ntype TraceTimelineEntry {\n sequence: Int!\n atSecond: Int!\n eventType: String!\n source: String!\n actorId: ID\n tag: String\n summary: String!\n}\n\ntype ReviewTraceQuality {\n eventCount: Int!\n modelGeneratedEventCount: Int!\n modelFailedEventCount: Int!\n voiceAudioEventCount: Int!\n blockedGuardrailCount: Int!\n unsafeEventCount: Int!\n missingRequiredTraceTagCount: Int!\n hasPatientNote: Boolean!\n hasModelProvenance: Boolean!\n}\n\ntype PatientNote {\n stationRunId: ID!\n submittedAtSecond: Int!\n text: String!\n}\n\ntype FacultyScoreDraft {\n reviewerId: ID!\n status: String!\n comments: String!\n}\n\ntype TraceEvent {\n stationRunId: ID!\n sequence: Int!\n eventType: String!\n occurredAt: String!\n atSecond: Int!\n source: String!\n actorId: ID\n tag: String\n \"\"\"\n Runtime/debug payload only. Faculty/admin review surfaces should use review-safe\n summary projections instead of this raw payload field.\n \"\"\"\n payload: JSON! @deprecated(reason: \"Use review-safe replay summaries for faculty/admin UI; raw payload is not review-safe.\")\n}\n\ninput AssembleExamFormInput {\n examFormId: ID!\n blueprintId: ID\n scenarioIds: [ID!]!\n}\n\ninput CreateStationRunQueueSnapshotInput {\n snapshotId: ID\n createdAt: String\n reviewerId: ID\n}\n\ninput ScenarioReviewDecisionInput {\n scenarioId: ID!\n version: Int!\n reviewerRole: String!\n reviewerId: ID!\n decision: ReviewDecision!\n comments: String!\n evidenceRefs: [String!]!\n}\n\ninput FacultyScoreDraftInput {\n stationRunId: ID!\n reviewerId: ID!\n comments: String!\n rubricScores: JSON!\n}\n"; +export const openClinXrAdminSchemaSdl = "scalar JSON\n\nenum ScenarioStatus {\n DRAFT\n READY_FOR_REVIEW\n APPROVED\n ARCHIVED\n}\n\nenum ReviewDecision {\n APPROVED\n CHANGES_REQUESTED\n}\n\ntype Query {\n scenario(scenarioId: ID!, version: Int): Scenario\n scenarios(status: ScenarioStatus): [Scenario!]!\n scenarioReviewDecisions(scenarioId: ID!, version: Int!): [ScenarioReviewDecisionRecord!]!\n examForm(examFormId: ID!): ExamForm\n stationRunQueueSnapshots(blueprintId: ID!): [StationRunQueueSnapshot!]!\n reviewPacket(stationRunId: ID!): ReviewPacket\n clinicalEventReviewSummary(stationRunId: ID!): DurableClinicalEventReviewSummary!\n reviewReplayReadinessSummary(stationRunId: ID!): ReviewReplayReadinessSummary!\n traceEvents(stationRunId: ID!): [TraceEvent!]!\n assetReadiness(scenarioId: ID!, version: Int!): AssetReadiness!\n assembledExamFacultyDisposition(examRunId: ID!): FacultyDispositionTrail\n}\n\ntype Mutation {\n assembleExamForm(input: AssembleExamFormInput!): ExamForm!\n createStationRunQueueSnapshot(input: CreateStationRunQueueSnapshotInput!): StationRunQueueSnapshot!\n submitScenarioReview(input: ScenarioReviewDecisionInput!): Scenario!\n saveFacultyScoreDraft(input: FacultyScoreDraftInput!): ReviewPacket!\n appendAssembledExamFacultyDisposition(input: AppendFacultyDispositionInput!): AppendFacultyDispositionResult!\n}\n\ntype Scenario {\n scenarioId: ID!\n version: Int!\n title: String!\n status: ScenarioStatus!\n clinicalObjectives: [String!]!\n actors: [Actor!]!\n requiredTraceTags: [String!]!\n review: ScenarioReviewState!\n governance: ScenarioGovernance!\n environment: ScenarioEnvironment\n equipment: [String!]!\n assetNeeds: [AssetNeed!]!\n}\n\ntype Actor {\n actorId: ID!\n role: String!\n displayName: String!\n demeanor: String\n communicationProfile: CommunicationProfile\n}\n\ntype CommunicationProfile {\n styleFamily: String!\n style: String!\n intensity: Float!\n baselineMood: [String!]!\n communicativeness: String!\n topicsToAvoid: [String!]!\n adverseResponse: String!\n deescalationTriggers: [String!]!\n escalationTriggers: [String!]!\n culturalLanguageNotes: [String!]!\n}\n\ntype ScenarioReviewState {\n clinical: String!\n psychometric: String!\n legal: String!\n simulationQa: String!\n}\n\ntype ScenarioReviewDecisionRecord {\n scenarioId: ID!\n version: Int!\n reviewerRole: String!\n reviewerId: ID!\n decision: String!\n comments: String!\n evidenceRefs: [String!]!\n reviewedAt: String!\n}\n\ntype ScenarioGovernance {\n scoreUseLabel: String!\n syntheticCaseDisclosure: String!\n validationStage: String!\n validationLimitations: [String!]!\n requiredReviewerRoles: [String!]!\n sourceIds: [String!]!\n safetyCriticalTraceTags: [String!]!\n hiddenFactPolicy: HiddenFactPolicy!\n}\n\ntype HiddenFactPolicy {\n learnerView: String!\n disclosureRequiresTrigger: Boolean!\n}\n\ntype ScenarioEnvironment {\n environmentId: ID!\n name: String!\n description: String!\n}\n\ntype AssetNeed {\n assetId: ID!\n assetType: String!\n description: String!\n licenseStatus: String!\n}\n\ntype AssetReadiness {\n scenarioId: ID!\n devReady: Boolean!\n productionReady: Boolean!\n missingRequiredAssetIds: [ID!]!\n blockedAssets: [AssetBlocker!]!\n productionBlockedAssets: [AssetBlocker!]!\n productionReadinessLadder: ScenarioAssetProductionReadinessLadder!\n}\n\ntype AssetBlocker {\n assetId: ID!\n blockers: [String!]!\n}\n\ntype ScenarioAssetProductionReadinessLadder {\n scenarioId: ID!\n productionReady: Boolean!\n assetCount: Int!\n productionReadyAssetIds: [ID!]!\n blockedAssetIds: [ID!]!\n missingRequiredAssetIds: [ID!]!\n blockers: [String!]!\n stationBudget: ScenarioAssetBudget!\n assetLadders: [AssetProductionReadinessLadder!]!\n}\n\ntype ScenarioAssetBudget {\n maxVisibleTriangles: Int!\n maxTextureMegabytes: Int!\n maxDrawCalls: Int!\n totalTriangles: Int!\n totalTextureMegabytes: Int!\n totalDrawCalls: Int!\n blockers: [String!]!\n}\n\ntype AssetProductionReadinessLadder {\n assetId: ID!\n scenarioId: ID!\n productionReady: Boolean!\n blockers: [String!]!\n steps: [AssetProductionReadinessStep!]!\n}\n\ntype AssetProductionReadinessStep {\n step: String!\n status: String!\n evidenceRefs: [String!]!\n blockers: [String!]!\n}\n\ntype ExamForm {\n examFormId: ID!\n blueprintId: ID!\n status: String!\n stationRefs: [StationRef!]!\n coverage: JSON!\n}\n\ntype StationRef {\n order: Int!\n scenarioId: ID!\n scenarioVersion: Int!\n title: String!\n}\n\ntype StationRunQueueSnapshot {\n snapshotId: ID!\n createdAt: String!\n reviewerId: ID\n queue: StationRunQueue!\n}\n\ntype StationRunQueue {\n blueprintId: ID!\n canStartLearnerExam: Boolean!\n stationQueue: [StationRunQueueItem!]!\n breakCheckpoints: [ExamBreakCheckpoint!]!\n totalStationTimeSeconds: Int!\n summary: StationRunQueueSummary!\n}\n\ntype StationRunQueueItem {\n stationOrder: Int!\n slotId: ID!\n label: String!\n scenarioId: ID\n scenarioVersion: Int\n status: String!\n blockers: [String!]!\n timing: ExamStationTimingWindow!\n}\n\ntype ExamStationTimingWindow {\n stationOrder: Int!\n slotId: ID!\n label: String!\n doorway: ExamTimingWindow!\n encounter: ExamTimingWindow!\n note: ExamTimingWindow!\n}\n\ntype ExamTimingWindow {\n startsAtSecond: Int!\n endsAtSecond: Int!\n durationSeconds: Int!\n}\n\ntype ExamBreakCheckpoint {\n afterStationOrder: Int!\n atSecond: Int!\n}\n\ntype StationRunQueueSummary {\n activationReady: Int!\n draftBlocked: Int!\n governanceBlocked: Int!\n missingScenario: Int!\n}\n\nenum ReviewPacketDialogueEmotion {\n anxious\n concerned\n reassured\n neutral\n}\n\nenum ReviewPacketEmotionEventKind {\n learner_empathetic\n learner_dismissive\n learner_interruption\n actor_silence_timeout\n learner_acknowledgement\n learner_clinical_question\n learner_personal_question\n learner_unclassified\n}\n\nenum ReviewPacketInterruptionKind {\n none\n truncated\n replaced\n}\n\ntype ReviewPacketActorTurnPlan {\n planId: ID!\n spokenText: String!\n dialogueEmotionFrom: ReviewPacketDialogueEmotion!\n dialogueEmotionTo: ReviewPacketDialogueEmotion!\n performancePlanId: ID!\n facePresetId: ID!\n eventKind: ReviewPacketEmotionEventKind!\n droppedTags: [String!]!\n claimScope: String!\n}\n\ntype ReviewPacketActorTurnExecution {\n planId: ID!\n truncated: Boolean!\n visemeCueCount: Int!\n ttsProviderId: String!\n interruptionKind: ReviewPacketInterruptionKind!\n}\n\ntype ReviewPacketActorTurn {\n plan: ReviewPacketActorTurnPlan!\n execution: ReviewPacketActorTurnExecution!\n}\n\ntype ReviewPacketEmotionalTimelineEntry {\n turnIndex: Int\n actorId: ID\n from: String!\n to: String!\n trigger: String\n planId: ID\n atSecond: Int\n}\n\ntype ReviewPacket {\n stationRunId: ID!\n scenarioId: ID!\n observedTraceTags: [String!]!\n missingRequiredTraceTags: [String!]!\n lateTraceTags: [String!]!\n unsafeEvents: [String!]!\n timeline: [TraceTimelineEntry!]!\n traceQuality: ReviewTraceQuality!\n patientNote: PatientNote\n facultyScoreDraft: FacultyScoreDraft!\n actorTurns: [ReviewPacketActorTurn!]!\n emotionalTimeline: [ReviewPacketEmotionalTimelineEntry!]!\n prosodyNeutralized: Boolean!\n}\n\ntype DurableClinicalEventReviewSummary {\n stationRunId: ID\n eventCount: Int!\n redactedEventCount: Int!\n clinicalEventKinds: JSON!\n traceTags: [String!]!\n statusCounts: JSON!\n latestAtSecond: Int\n durableStore: String\n safeForFacultyReview: Boolean!\n}\n\ntype ReviewReplayReadinessSummary {\n stationRunId: ID\n replayEvidenceReady: Boolean!\n facultyReviewSafe: Boolean!\n timelineEntryCount: Int!\n traceEventCount: Int!\n durableEventCount: Int!\n redactedDurableEventCount: Int!\n missingRequiredBehaviorCount: Int!\n lateBehaviorCount: Int!\n safetySignalCount: Int!\n blockers: [String!]!\n recommendedNextAction: String!\n replayBoundary: String!\n runtimeVisualEvidenceReplayProjection: RuntimeVisualEvidenceReplayProjection\n}\n\ntype RuntimeVisualEvidenceReplayProjection {\n schemaVersion: String!\n source: String!\n stationRunId: ID!\n scenarioId: ID!\n reviewedMetadataOnlyCount: Int!\n heldMetadataOnlyCount: Int!\n acceptedAttachmentRefCount: Int!\n runtimeEvidenceRefCount: Int!\n visualQaEvidenceRefCount: Int!\n acceptedActionIds: [String!]!\n rawPayloadDisplayed: Boolean!\n providerExecutionAllowed: Boolean!\n runtimeExecutionAllowed: Boolean!\n learnerLaunchAllowed: Boolean!\n questEvidenceRefreshAllowed: Boolean!\n productionAssetReadinessClaimed: Boolean!\n clinicalValidityClaimed: Boolean!\n scoringValidityClaimed: Boolean!\n replayEvidenceReady: Boolean!\n blockerIds: [String!]!\n nextActions: [String!]!\n uiXrConsumerOperatorWorkflowSummary: UiXrConsumerOperatorWorkflowSummary\n claimBoundary: String!\n notEvidenceFor: [String!]!\n}\n\ntype UiXrConsumerOperatorWorkflowSummary {\n schemaVersion: String!\n source: String!\n scenarioId: ID!\n acceptedAttachmentRefCount: Int!\n runtimeEvidenceRefCount: Int!\n visualQaEvidenceRefCount: Int!\n targetRoute: String!\n method: String!\n submitBodyRef: String!\n submitPreview: UiXrConsumerWorkflowSubmitPreview!\n reviewerAction: String!\n preflightChecks: [String!]!\n nextActions: [String!]!\n rawPayloadDisplayed: Boolean!\n providerExecutionAllowed: Boolean!\n runtimeExecutionAllowed: Boolean!\n learnerLaunchAllowed: Boolean!\n questEvidenceRefreshAllowed: Boolean!\n productionAssetReadinessClaimed: Boolean!\n clinicalValidityClaimed: Boolean!\n scoringValidityClaimed: Boolean!\n blockerIds: [String!]!\n claimBoundary: String!\n notEvidenceFor: [String!]!\n}\n\ntype UiXrConsumerWorkflowSubmitPreview {\n route: String!\n bodyRef: String!\n attachmentCount: Int!\n actionIds: [String!]!\n inputIds: [String!]!\n localArtifactPaths: [String!]!\n rawPayloadDisplayed: Boolean!\n claimBoundary: String!\n}\n\ntype TraceTimelineEntry {\n sequence: Int!\n atSecond: Int!\n eventType: String!\n source: String!\n actorId: ID\n tag: String\n summary: String!\n}\n\ntype ReviewTraceQuality {\n eventCount: Int!\n modelGeneratedEventCount: Int!\n modelFailedEventCount: Int!\n voiceAudioEventCount: Int!\n blockedGuardrailCount: Int!\n unsafeEventCount: Int!\n missingRequiredTraceTagCount: Int!\n hasPatientNote: Boolean!\n hasModelProvenance: Boolean!\n}\n\ntype PatientNote {\n stationRunId: ID!\n submittedAtSecond: Int!\n text: String!\n}\n\ntype FacultyScoreDraft {\n reviewerId: ID!\n status: String!\n comments: String!\n}\n\ntype TraceEvent {\n stationRunId: ID!\n sequence: Int!\n eventType: String!\n occurredAt: String!\n atSecond: Int!\n source: String!\n actorId: ID\n tag: String\n \"\"\"\n Runtime/debug payload only. Faculty/admin review surfaces should use review-safe\n summary projections instead of this raw payload field.\n \"\"\"\n payload: JSON! @deprecated(reason: \"Use review-safe replay summaries for faculty/admin UI; raw payload is not review-safe.\")\n}\n\ninput AssembleExamFormInput {\n examFormId: ID!\n blueprintId: ID\n scenarioIds: [ID!]!\n}\n\ninput CreateStationRunQueueSnapshotInput {\n snapshotId: ID\n createdAt: String\n reviewerId: ID\n}\n\ninput ScenarioReviewDecisionInput {\n scenarioId: ID!\n version: Int!\n reviewerRole: String!\n reviewerId: ID!\n decision: ReviewDecision!\n comments: String!\n evidenceRefs: [String!]!\n}\n\ninput FacultyScoreDraftInput {\n stationRunId: ID!\n reviewerId: ID!\n comments: String!\n rubricScores: JSON!\n}\n\nenum FacultyDispositionValue {\n hold\n needs_revision\n local_debrief_ready\n}\n\nenum FacultyDispositionStatus {\n draft\n final\n}\n\nenum FacultyDispositionRefusalCode {\n stale_packet_digest\n producer_self_review\n identity_mutation\n overwrite_refused\n finalized\n}\n\ntype AssembledExamEvidencePacket {\n examRunId: ID!\n packetDigest: String!\n learnerId: ID\n stationRunIds: [ID!]!\n claimBoundary: String!\n notEvidenceFor: [String!]!\n examEquivalenceGate: Boolean!\n}\n\ntype FacultyDispositionDecision {\n decisionId: ID!\n examRunId: ID!\n reviewerId: ID!\n packetDigest: String!\n disposition: FacultyDispositionValue!\n status: FacultyDispositionStatus!\n rationale: String!\n attestedAt: String!\n sequence: Int!\n}\n\ntype FacultyDispositionTrail {\n examRunId: ID!\n packetDigest: String!\n evidencePacket: AssembledExamEvidencePacket!\n decisions: [FacultyDispositionDecision!]!\n current: FacultyDispositionDecision\n claimBoundary: String!\n notEvidenceFor: [String!]!\n scoringValidityClaimed: Boolean!\n examEquivalenceGate: Boolean!\n}\n\ninterface FacultyDispositionRefusal {\n code: FacultyDispositionRefusalCode!\n reason: String!\n notEvidenceFor: [String!]!\n scoringValidityClaimed: Boolean!\n examEquivalenceGate: Boolean!\n}\n\ntype FacultyDispositionStaleDigest implements FacultyDispositionRefusal {\n code: FacultyDispositionRefusalCode!\n reason: String!\n notEvidenceFor: [String!]!\n scoringValidityClaimed: Boolean!\n examEquivalenceGate: Boolean!\n}\n\ntype FacultyDispositionProducerSelfReview implements FacultyDispositionRefusal {\n code: FacultyDispositionRefusalCode!\n reason: String!\n notEvidenceFor: [String!]!\n scoringValidityClaimed: Boolean!\n examEquivalenceGate: Boolean!\n}\n\ntype FacultyDispositionIdentityMutation implements FacultyDispositionRefusal {\n code: FacultyDispositionRefusalCode!\n reason: String!\n notEvidenceFor: [String!]!\n scoringValidityClaimed: Boolean!\n examEquivalenceGate: Boolean!\n}\n\ntype FacultyDispositionOverwriteRefused implements FacultyDispositionRefusal {\n code: FacultyDispositionRefusalCode!\n reason: String!\n notEvidenceFor: [String!]!\n scoringValidityClaimed: Boolean!\n examEquivalenceGate: Boolean!\n}\n\ntype FacultyDispositionPostFinalization implements FacultyDispositionRefusal {\n code: FacultyDispositionRefusalCode!\n reason: String!\n notEvidenceFor: [String!]!\n scoringValidityClaimed: Boolean!\n examEquivalenceGate: Boolean!\n}\n\nunion AppendFacultyDispositionResult =\n FacultyDispositionTrail\n | FacultyDispositionStaleDigest\n | FacultyDispositionProducerSelfReview\n | FacultyDispositionIdentityMutation\n | FacultyDispositionOverwriteRefused\n | FacultyDispositionPostFinalization\n\ninput AppendFacultyDispositionInput {\n examRunId: ID!\n reviewerId: ID!\n packetDigest: String!\n disposition: FacultyDispositionValue!\n status: FacultyDispositionStatus!\n rationale: String!\n attestedAt: String!\n decisionId: ID\n learnerId: ID\n evidencePacket: JSON\n decisions: JSON\n}\n"; diff --git a/packages/openclinxr/graphql/src/index.ts b/packages/openclinxr/graphql/src/index.ts index 12f6212a..14f1999c 100644 --- a/packages/openclinxr/graphql/src/index.ts +++ b/packages/openclinxr/graphql/src/index.ts @@ -4,7 +4,11 @@ import { adminGraphqlFieldResolver, openClinXrAdminSchemaSdl } from "./schema.js export { type AdminGraphqlDocument, adminGraphqlDocumentByOperationName, adminGraphqlDocuments } from "./documents.js"; export { adminGraphqlFieldResolver, + FACULTY_DISPOSITION_CLAIM_BOUNDARY, + FACULTY_DISPOSITION_NOT_EVIDENCE_FOR, openClinXrAdminSchemaSdl, + projectAppendFacultyDispositionResult, + projectFacultyDispositionTrail, projectReviewPacketActorTurnLayers, REVIEW_PACKET_ACTOR_TURN_CLAIM_SCOPE, } from "./schema.js"; @@ -12,9 +16,11 @@ export { import type { AssetReadiness, DurableClinicalEventReviewSummary, + MutationAppendAssembledExamFacultyDispositionArgs, MutationCreateStationRunQueueSnapshotArgs, MutationSaveFacultyScoreDraftArgs, MutationSubmitScenarioReviewArgs, + QueryAssembledExamFacultyDispositionArgs, QueryAssetReadinessArgs, QueryClinicalEventReviewSummaryArgs, QueryReviewPacketArgs, @@ -95,6 +101,12 @@ export type AdminGraphqlRootValue = { saveFacultyScoreDraft?: ( args: MutationSaveFacultyScoreDraftArgs, ) => Promise> | ReviewPacket | Record; + assembledExamFacultyDisposition?: ( + args: QueryAssembledExamFacultyDispositionArgs, + ) => Promise | null | undefined> | Record | null | undefined; + appendAssembledExamFacultyDisposition?: ( + args: MutationAppendAssembledExamFacultyDispositionArgs, + ) => Promise> | Record; }; export function buildAdminGraphqlSchema(): GraphQLSchema { diff --git a/packages/openclinxr/graphql/src/schema.graphql b/packages/openclinxr/graphql/src/schema.graphql index 9d894d06..35a54bfe 100644 --- a/packages/openclinxr/graphql/src/schema.graphql +++ b/packages/openclinxr/graphql/src/schema.graphql @@ -23,6 +23,7 @@ type Query { reviewReplayReadinessSummary(stationRunId: ID!): ReviewReplayReadinessSummary! traceEvents(stationRunId: ID!): [TraceEvent!]! assetReadiness(scenarioId: ID!, version: Int!): AssetReadiness! + assembledExamFacultyDisposition(examRunId: ID!): FacultyDispositionTrail } type Mutation { @@ -30,6 +31,7 @@ type Mutation { createStationRunQueueSnapshot(input: CreateStationRunQueueSnapshotInput!): StationRunQueueSnapshot! submitScenarioReview(input: ScenarioReviewDecisionInput!): Scenario! saveFacultyScoreDraft(input: FacultyScoreDraftInput!): ReviewPacket! + appendAssembledExamFacultyDisposition(input: AppendFacultyDispositionInput!): AppendFacultyDispositionResult! } type Scenario { @@ -483,3 +485,126 @@ input FacultyScoreDraftInput { comments: String! rubricScores: JSON! } + +enum FacultyDispositionValue { + hold + needs_revision + local_debrief_ready +} + +enum FacultyDispositionStatus { + draft + final +} + +enum FacultyDispositionRefusalCode { + stale_packet_digest + producer_self_review + identity_mutation + overwrite_refused + finalized +} + +type AssembledExamEvidencePacket { + examRunId: ID! + packetDigest: String! + learnerId: ID + stationRunIds: [ID!]! + claimBoundary: String! + notEvidenceFor: [String!]! + examEquivalenceGate: Boolean! +} + +type FacultyDispositionDecision { + decisionId: ID! + examRunId: ID! + reviewerId: ID! + packetDigest: String! + disposition: FacultyDispositionValue! + status: FacultyDispositionStatus! + rationale: String! + attestedAt: String! + sequence: Int! +} + +type FacultyDispositionTrail { + examRunId: ID! + packetDigest: String! + evidencePacket: AssembledExamEvidencePacket! + decisions: [FacultyDispositionDecision!]! + current: FacultyDispositionDecision + claimBoundary: String! + notEvidenceFor: [String!]! + scoringValidityClaimed: Boolean! + examEquivalenceGate: Boolean! +} + +interface FacultyDispositionRefusal { + code: FacultyDispositionRefusalCode! + reason: String! + notEvidenceFor: [String!]! + scoringValidityClaimed: Boolean! + examEquivalenceGate: Boolean! +} + +type FacultyDispositionStaleDigest implements FacultyDispositionRefusal { + code: FacultyDispositionRefusalCode! + reason: String! + notEvidenceFor: [String!]! + scoringValidityClaimed: Boolean! + examEquivalenceGate: Boolean! +} + +type FacultyDispositionProducerSelfReview implements FacultyDispositionRefusal { + code: FacultyDispositionRefusalCode! + reason: String! + notEvidenceFor: [String!]! + scoringValidityClaimed: Boolean! + examEquivalenceGate: Boolean! +} + +type FacultyDispositionIdentityMutation implements FacultyDispositionRefusal { + code: FacultyDispositionRefusalCode! + reason: String! + notEvidenceFor: [String!]! + scoringValidityClaimed: Boolean! + examEquivalenceGate: Boolean! +} + +type FacultyDispositionOverwriteRefused implements FacultyDispositionRefusal { + code: FacultyDispositionRefusalCode! + reason: String! + notEvidenceFor: [String!]! + scoringValidityClaimed: Boolean! + examEquivalenceGate: Boolean! +} + +type FacultyDispositionPostFinalization implements FacultyDispositionRefusal { + code: FacultyDispositionRefusalCode! + reason: String! + notEvidenceFor: [String!]! + scoringValidityClaimed: Boolean! + examEquivalenceGate: Boolean! +} + +union AppendFacultyDispositionResult = + FacultyDispositionTrail + | FacultyDispositionStaleDigest + | FacultyDispositionProducerSelfReview + | FacultyDispositionIdentityMutation + | FacultyDispositionOverwriteRefused + | FacultyDispositionPostFinalization + +input AppendFacultyDispositionInput { + examRunId: ID! + reviewerId: ID! + packetDigest: String! + disposition: FacultyDispositionValue! + status: FacultyDispositionStatus! + rationale: String! + attestedAt: String! + decisionId: ID + learnerId: ID + evidencePacket: JSON + decisions: JSON +} diff --git a/packages/openclinxr/graphql/src/schema.ts b/packages/openclinxr/graphql/src/schema.ts index f3bb153f..1f74925e 100644 --- a/packages/openclinxr/graphql/src/schema.ts +++ b/packages/openclinxr/graphql/src/schema.ts @@ -1,6 +1,16 @@ import { defaultFieldResolver, type GraphQLFieldResolver } from "graphql"; +import { + projectAppendFacultyDispositionResult, + projectFacultyDispositionTrail, +} from "./faculty-disposition.js"; export { openClinXrAdminSchemaSdl } from "./generated/schema.generated.js"; +export { + FACULTY_DISPOSITION_CLAIM_BOUNDARY, + FACULTY_DISPOSITION_NOT_EVIDENCE_FOR, + projectAppendFacultyDispositionResult, + projectFacultyDispositionTrail, +} from "./faculty-disposition.js"; const DIALOGUE_EMOTIONS = new Set(["anxious", "concerned", "reassured", "neutral"]); const EVENT_KINDS = new Set([ @@ -90,7 +100,14 @@ export const adminGraphqlFieldResolver: GraphQLFieldResolver = return layers.prosodyNeutralized; } } - return defaultFieldResolver(source, args, context, info); + const raw = defaultFieldResolver(source, args, context, info); + if (info.parentType.name === "Query" && info.fieldName === "assembledExamFacultyDisposition") { + return Promise.resolve(raw).then(projectFacultyDispositionTrail); + } + if (info.parentType.name === "Mutation" && info.fieldName === "appendAssembledExamFacultyDisposition") { + return Promise.resolve(raw).then(projectAppendFacultyDispositionResult); + } + return raw; }; function projectActorTurns(packet: Record): ReviewPacketActorTurnGraphql[] { diff --git a/packages/openclinxr/graphql/src/the-faculty-disposition-contract-is-append-only.test.ts b/packages/openclinxr/graphql/src/the-faculty-disposition-contract-is-append-only.test.ts new file mode 100644 index 00000000..9d8ec7fe --- /dev/null +++ b/packages/openclinxr/graphql/src/the-faculty-disposition-contract-is-append-only.test.ts @@ -0,0 +1,306 @@ +import { readFileSync } from "node:fs"; +import { GraphQLObjectType, GraphQLUnionType, parse, validate } from "graphql"; +import { describe, expect, it } from "vitest"; +import { buildAdminGraphqlSchema, executeAdminGraphql, type AdminGraphqlRootValue } from "./index.js"; +import { + FACULTY_DISPOSITION_CLAIM_BOUNDARY, + FACULTY_DISPOSITION_NOT_EVIDENCE_FOR, +} from "./schema.js"; + +const EXAM_RUN_ID = "exam_run_faculty_disposition_graphql_001"; +const LEARNER_ID = "learner_phase_001"; +const REVIEWER_ID = "faculty_disposition_001"; +const PRODUCER_FACULTY_ID = "faculty_001"; +const DIGEST = "packet-digest-frozen"; +const ATTESTED_AT = "2026-09-04T10:00:00.000Z"; +const DOCUMENT_SOURCE = readFileSync(new URL("./documents/faculty-disposition.graphql", import.meta.url), "utf8"); + +describe("faculty disposition GraphQL contract is append-only", () => { + it("exposes query and mutation fields beside an immutable evidence object", () => { + const schema = buildAdminGraphqlSchema(); + expect(schema.getQueryType()?.getFields()).toHaveProperty("assembledExamFacultyDisposition"); + expect(schema.getMutationType()?.getFields()).toHaveProperty("appendAssembledExamFacultyDisposition"); + expect(schema.getType("FacultyDispositionTrail")).toBeInstanceOf(GraphQLObjectType); + expect(schema.getType("AssembledExamEvidencePacket")).toBeInstanceOf(GraphQLObjectType); + expect(schema.getType("AppendFacultyDispositionResult")).toBeInstanceOf(GraphQLUnionType); + const trail = schema.getType("FacultyDispositionTrail") as GraphQLObjectType; + expect(trail.getFields()).toHaveProperty("evidencePacket"); + expect(trail.getFields()).toHaveProperty("decisions"); + expect(trail.getFields()).toHaveProperty("current"); + expect(validate(schema, parse(DOCUMENT_SOURCE))).toEqual([]); + }); + + it("appends draft then final without mutating the evidence packet", async () => { + const store = createDispositionStore(); + const draft = await append(store, { + disposition: "hold", + status: "draft", + rationale: "Hold for faculty debrief; no score use.", + }); + expect(draft.errors).toBeUndefined(); + const draftTrail = trailOf(draft); + expect(draftTrail.__typename).toBe("FacultyDispositionTrail"); + expect(draftTrail.packetDigest).toBe(DIGEST); + expect(draftTrail.evidencePacket).toMatchObject({ + examRunId: EXAM_RUN_ID, + packetDigest: DIGEST, + learnerId: LEARNER_ID, + stationRunIds: ["run_ed_001"], + examEquivalenceGate: false, + }); + expect(draftTrail.decisions).toHaveLength(1); + expect(draftTrail.current).toMatchObject({ + reviewerId: REVIEWER_ID, + disposition: "hold", + status: "draft", + sequence: 1, + }); + expect(draftTrail.scoringValidityClaimed).toBe(false); + expect(draftTrail.examEquivalenceGate).toBe(false); + expect(draftTrail.claimBoundary).toBe(FACULTY_DISPOSITION_CLAIM_BOUNDARY); + expect(draftTrail.notEvidenceFor).toEqual([...FACULTY_DISPOSITION_NOT_EVIDENCE_FOR]); + expect(draftTrail.decisions).not.toBe(draftTrail.evidencePacket); + const evidenceSnapshot = structuredClone(draftTrail.evidencePacket); + + const finalized = await append(store, { + disposition: "local_debrief_ready", + status: "final", + rationale: "Final local debrief only.", + attestedAt: "2026-09-04T11:00:00.000Z", + }); + expect(finalized.errors).toBeUndefined(); + const finalTrail = trailOf(finalized); + expect(finalTrail.decisions).toHaveLength(2); + expect(finalTrail.decisions[0]).toMatchObject({ status: "draft", sequence: 1 }); + expect(finalTrail.decisions[1]).toMatchObject({ + status: "final", + sequence: 2, + disposition: "local_debrief_ready", + }); + expect(finalTrail.evidencePacket).toEqual(evidenceSnapshot); + expect(finalTrail.scoringValidityClaimed).toBe(false); + expect(JSON.stringify(finalTrail)).not.toContain("hiddenFacts"); + + const queried = await executeAdminGraphql( + { + query: DOCUMENT_SOURCE, + operationName: "AssembledExamFacultyDisposition", + variables: { examRunId: EXAM_RUN_ID }, + }, + store, + ); + expect(queried.errors).toBeUndefined(); + const readTrail = queried.data?.["assembledExamFacultyDisposition"] as ReturnType; + expect(readTrail.decisions).toHaveLength(2); + expect(readTrail.current).toMatchObject({ status: "final" }); + expect(readTrail.evidencePacket).toEqual(evidenceSnapshot); + }); + + it("returns typed refusals for stale digest, producer self-review, identity mutation, overwrite, and post-finalization", async () => { + const store = createDispositionStore(); + + const producer = await append(store, { reviewerId: LEARNER_ID }); + expect(refusalOf(producer)).toMatchObject({ + __typename: "FacultyDispositionProducerSelfReview", + code: "producer_self_review", + scoringValidityClaimed: false, + examEquivalenceGate: false, + }); + + const producerFaculty = await append(store, { reviewerId: PRODUCER_FACULTY_ID }); + expect(refusalOf(producerFaculty).__typename).toBe("FacultyDispositionProducerSelfReview"); + + const stale = await append(store, { packetDigest: "not-the-digest" }); + expect(refusalOf(stale)).toMatchObject({ + __typename: "FacultyDispositionStaleDigest", + code: "stale_packet_digest", + notEvidenceFor: [...FACULTY_DISPOSITION_NOT_EVIDENCE_FOR], + }); + + const first = await append(store, {}); + expect(trailOf(first).decisions).toHaveLength(1); + const firstId = trailOf(first).current?.decisionId as string; + + const mutatedReviewer = await append(store, { reviewerId: "faculty_disposition_002" }); + expect(refusalOf(mutatedReviewer)).toMatchObject({ + __typename: "FacultyDispositionIdentityMutation", + code: "identity_mutation", + }); + + const overwriteId = await append(store, { decisionId: firstId }); + expect(refusalOf(overwriteId)).toMatchObject({ + __typename: "FacultyDispositionOverwriteRefused", + code: "overwrite_refused", + }); + + const overwriteEvidence = await append(store, { + evidencePacket: { examRunId: "mutated" }, + }); + expect(refusalOf(overwriteEvidence).__typename).toBe("FacultyDispositionOverwriteRefused"); + + const finalize = await append(store, { + status: "final", + disposition: "local_debrief_ready", + rationale: "Close the trail.", + }); + expect(trailOf(finalize).current?.status).toBe("final"); + + const afterFinal = await append(store, { + status: "draft", + rationale: "try again", + }); + expect(refusalOf(afterFinal)).toMatchObject({ + __typename: "FacultyDispositionPostFinalization", + code: "finalized", + scoringValidityClaimed: false, + examEquivalenceGate: false, + }); + + const reread = await executeAdminGraphql( + { + query: DOCUMENT_SOURCE, + operationName: "AssembledExamFacultyDisposition", + variables: { examRunId: EXAM_RUN_ID }, + }, + store, + ); + const remaining = reread.data?.["assembledExamFacultyDisposition"] as { decisions: unknown[]; evidencePacket: { examRunId: string } }; + expect(remaining.decisions).toHaveLength(2); + expect(remaining.evidencePacket.examRunId).toBe(EXAM_RUN_ID); + }); +}); + +function createDispositionStore(): AdminGraphqlRootValue { + const evidencePacket = { + examRunId: EXAM_RUN_ID, + learnerId: LEARNER_ID, + stations: [ + { + reviewPacket: { + stationRunId: "run_ed_001", + facultyScoreDraft: { reviewerId: PRODUCER_FACULTY_ID }, + hiddenFacts: ["HIDDEN_DIAGNOSIS"], + }, + }, + ], + claimBoundary: "assembled_exam_review_packet_not_exam_equivalence", + notEvidenceFor: ["exam_equivalence"], + examEquivalenceGate: false, + }; + let decisions: Array> = []; + + return { + assembledExamFacultyDisposition: ({ examRunId }) => { + if (examRunId !== EXAM_RUN_ID) { + return null; + } + return readModel(evidencePacket, decisions); + }, + appendAssembledExamFacultyDisposition: ({ input }) => { + const body = input as Record; + if (body["evidencePacket"] != null || body["decisions"] != null) { + return restError("overwrite_refused", "cannot_replace_evidence_or_trail"); + } + const reviewerId = String(body["reviewerId"] ?? ""); + const packetDigest = String(body["packetDigest"] ?? ""); + if (reviewerId === LEARNER_ID || reviewerId === PRODUCER_FACULTY_ID) { + return restError("producer_self_review", "reviewer_is_producer"); + } + if (packetDigest !== DIGEST) { + return restError("stale_packet_digest", "packet_digest_mismatch"); + } + const last = decisions[decisions.length - 1]; + if (last?.["status"] === "final") { + return restError("finalized", "disposition_already_final"); + } + const lockedReviewer = decisions[0]?.["reviewerId"]; + if (typeof lockedReviewer === "string" && lockedReviewer !== reviewerId) { + return restError("identity_mutation", "reviewer_mismatch"); + } + const decisionId = typeof body["decisionId"] === "string" ? body["decisionId"] : ""; + if (decisionId.length > 0 && decisions.some((item) => item["decisionId"] === decisionId)) { + return restError("overwrite_refused", "decision_id_already_recorded"); + } + const sequence = decisions.length + 1; + const next = { + decisionId: decisionId.length > 0 ? decisionId : `assembled_exam_disposition:${EXAM_RUN_ID}:${sequence}`, + examRunId: EXAM_RUN_ID, + reviewerId, + packetDigest: DIGEST, + disposition: body["disposition"], + status: body["status"], + rationale: body["rationale"], + attestedAt: body["attestedAt"], + sequence, + }; + decisions = [...decisions, next]; + return readModel(evidencePacket, decisions); + }, + }; +} + +function readModel(evidencePacket: Record, decisions: Array>) { + return { + examRunId: EXAM_RUN_ID, + packetDigest: DIGEST, + evidencePacket, + decisions, + current: decisions[decisions.length - 1] ?? null, + claimBoundary: FACULTY_DISPOSITION_CLAIM_BOUNDARY, + notEvidenceFor: FACULTY_DISPOSITION_NOT_EVIDENCE_FOR, + scoringValidityClaimed: false, + examEquivalenceGate: false, + }; +} + +function restError(error: string, reason: string) { + return { error, reason, notEvidenceFor: [...FACULTY_DISPOSITION_NOT_EVIDENCE_FOR] }; +} + +async function append(rootValue: AdminGraphqlRootValue, overrides: Record) { + return executeAdminGraphql( + { + query: DOCUMENT_SOURCE, + operationName: "AppendAssembledExamFacultyDisposition", + variables: { + input: { + examRunId: EXAM_RUN_ID, + reviewerId: REVIEWER_ID, + packetDigest: DIGEST, + disposition: "hold", + status: "draft", + rationale: "Hold for faculty debrief; no score use.", + attestedAt: ATTESTED_AT, + ...overrides, + }, + }, + }, + rootValue, + ); +} + +function trailOf(result: { data?: unknown; errors?: unknown }) { + const data = result.data as { appendAssembledExamFacultyDisposition: Record }; + return data.appendAssembledExamFacultyDisposition as { + __typename: string; + packetDigest: string; + evidencePacket: Record; + decisions: Array>; + current?: { decisionId?: string; status?: string; reviewerId?: string; disposition?: string; sequence?: number }; + scoringValidityClaimed: boolean; + examEquivalenceGate: boolean; + claimBoundary: string; + notEvidenceFor: string[]; + }; +} + +function refusalOf(result: { data?: unknown; errors?: unknown }) { + return trailOf(result) as unknown as { + __typename: string; + code: string; + reason: string; + notEvidenceFor: string[]; + scoringValidityClaimed: boolean; + examEquivalenceGate: boolean; + }; +}