@nexalink/coding-contracts 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/cjs/index.cjs +460 -16
- package/dist/index.d.ts +57 -5
- package/dist/index.js +456 -14
- package/package.json +8 -2
- package/schemas/coding-reviewer-action.schema.json +13 -2
- package/schemas/rcm-reviewer-event.schema.json +160 -0
- package/schemas/rcm-workflow-catalog.schema.json +62 -5
- package/versions.json +8 -3
- package/workflow-constants.json +2 -1
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ export const RCM_MODEL_POLICY_ID = "carecompass-rcm-review-v1";
|
|
|
41
41
|
export const RCM_HUMAN_REVIEW_POLICY_ID = "qualified-coder-review-v1";
|
|
42
42
|
export const RCM_OUTPUT_SCHEMA_NAME = "carecompass_rcm_grounded_review";
|
|
43
43
|
export const RCM_HUMAN_REVIEW_INSTRUCTION = "Qualified human coder review is required before billing, coding, or claim submission.";
|
|
44
|
+
export const PIN_TIME_WORKFLOW_ID = "monthly-pin-time-aggregation";
|
|
44
45
|
// Schema wire versions (source: schemas/*.schema.json).
|
|
45
46
|
export const CODING_CASE_CONTEXT_SCHEMA_VERSION = "coding-case-context-v1";
|
|
46
47
|
export const CODING_CASE_PAYER_SCHEMA_VERSION = "coding-case-payer-v1";
|
|
@@ -59,6 +60,7 @@ export const RCM_INPUT_SCHEMA_VERSION = "rcm-coding-input-v1";
|
|
|
59
60
|
export const RCM_OUTPUT_SCHEMA_VERSION = "rcm-grounded-human-review-v2";
|
|
60
61
|
export const RCM_MODEL_PROMPT_SCHEMA_VERSION = "rcm-model-prompt-v1";
|
|
61
62
|
export const RCM_RETRIEVAL_SCHEMA_VERSION = "rcm-retrieval-metadata-v1";
|
|
63
|
+
export const RCM_REVIEWER_EVENT_SCHEMA_VERSION = "rcm-reviewer-event-v1";
|
|
62
64
|
export const RCM_RUNTIME_EVENT_SCHEMA_VERSION = "rcm-runtime-event-v1";
|
|
63
65
|
export const RCM_WORKFLOW_CATALOG_SCHEMA_VERSION = "rcm-workflow-catalog-v1";
|
|
64
66
|
// --------------------------------------------------------------------
|
|
@@ -3166,7 +3168,7 @@ export const CodingRuleResultSchemaJson = {
|
|
|
3166
3168
|
},
|
|
3167
3169
|
};
|
|
3168
3170
|
// --------------------------------------------------------------------
|
|
3169
|
-
// CodingReviewerAction (schema 1.
|
|
3171
|
+
// CodingReviewerAction (schema 1.1.0, wire coding-reviewer-action-v1)
|
|
3170
3172
|
// --------------------------------------------------------------------
|
|
3171
3173
|
export const codingReviewerActionSchema = z
|
|
3172
3174
|
.object({
|
|
@@ -3179,6 +3181,15 @@ export const codingReviewerActionSchema = z
|
|
|
3179
3181
|
"escalated",
|
|
3180
3182
|
]),
|
|
3181
3183
|
resolutionCode: z.string().min(1).max(80).optional(),
|
|
3184
|
+
reviewOutcomeCategory: z
|
|
3185
|
+
.enum([
|
|
3186
|
+
"unsupported_or_excessive",
|
|
3187
|
+
"missed_supported",
|
|
3188
|
+
"unchanged_supported",
|
|
3189
|
+
"missing_evidence",
|
|
3190
|
+
"escalation",
|
|
3191
|
+
])
|
|
3192
|
+
.optional(),
|
|
3182
3193
|
notes: z
|
|
3183
3194
|
.string()
|
|
3184
3195
|
.min(1)
|
|
@@ -3202,6 +3213,7 @@ export function codingReviewerActionValidationError(value) {
|
|
|
3202
3213
|
"reviewerRole",
|
|
3203
3214
|
"action",
|
|
3204
3215
|
"resolutionCode",
|
|
3216
|
+
"reviewOutcomeCategory",
|
|
3205
3217
|
"notes",
|
|
3206
3218
|
"occurredAt",
|
|
3207
3219
|
].includes(key)))
|
|
@@ -3229,6 +3241,18 @@ export function codingReviewerActionValidationError(value) {
|
|
|
3229
3241
|
value_resolutionCode.length > 80)
|
|
3230
3242
|
return "reviewer_action_resolution_code";
|
|
3231
3243
|
}
|
|
3244
|
+
if (value["reviewOutcomeCategory"] !== undefined) {
|
|
3245
|
+
const value_reviewOutcomeCategory = value["reviewOutcomeCategory"];
|
|
3246
|
+
if (typeof value_reviewOutcomeCategory !== "string" ||
|
|
3247
|
+
![
|
|
3248
|
+
"unsupported_or_excessive",
|
|
3249
|
+
"missed_supported",
|
|
3250
|
+
"unchanged_supported",
|
|
3251
|
+
"missing_evidence",
|
|
3252
|
+
"escalation",
|
|
3253
|
+
].includes(value_reviewOutcomeCategory))
|
|
3254
|
+
return "reviewer_action_outcome_category";
|
|
3255
|
+
}
|
|
3232
3256
|
if (value["notes"] !== undefined) {
|
|
3233
3257
|
const value_notes = value["notes"];
|
|
3234
3258
|
if (typeof value_notes !== "string" ||
|
|
@@ -3250,8 +3274,8 @@ export const CodingReviewerActionSchemaJson = {
|
|
|
3250
3274
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
3251
3275
|
$id: "https://nexalink.care/contracts/coding-reviewer-action.schema.json",
|
|
3252
3276
|
title: "CodingReviewerAction",
|
|
3253
|
-
description: "Attributable human-review disposition appended to an otherwise immutable decision. A decision plus its reviewer actions remains replayable: original and reviewer versions are both preserved.",
|
|
3254
|
-
version: "1.
|
|
3277
|
+
description: "Attributable human-review disposition appended to an otherwise immutable decision. A decision plus its reviewer actions remains replayable: original and reviewer versions are both preserved. Review outcome categories are reviewer-adjudicated observations and never inferred from payment, code, or reimbursement changes.",
|
|
3278
|
+
version: "1.1.0",
|
|
3255
3279
|
type: "object",
|
|
3256
3280
|
additionalProperties: false,
|
|
3257
3281
|
required: ["reviewerId", "action", "occurredAt"],
|
|
@@ -3275,6 +3299,16 @@ export const CodingReviewerActionSchemaJson = {
|
|
|
3275
3299
|
minLength: 1,
|
|
3276
3300
|
maxLength: 80,
|
|
3277
3301
|
},
|
|
3302
|
+
reviewOutcomeCategory: {
|
|
3303
|
+
type: "string",
|
|
3304
|
+
enum: [
|
|
3305
|
+
"unsupported_or_excessive",
|
|
3306
|
+
"missed_supported",
|
|
3307
|
+
"unchanged_supported",
|
|
3308
|
+
"missing_evidence",
|
|
3309
|
+
"escalation",
|
|
3310
|
+
],
|
|
3311
|
+
},
|
|
3278
3312
|
notes: {
|
|
3279
3313
|
type: "string",
|
|
3280
3314
|
minLength: 1,
|
|
@@ -3466,7 +3500,7 @@ export const codingDecisionSchema = z
|
|
|
3466
3500
|
});
|
|
3467
3501
|
}
|
|
3468
3502
|
});
|
|
3469
|
-
export function codingDecisionValidationError(value
|
|
3503
|
+
export function codingDecisionValidationError(value) {
|
|
3470
3504
|
if (!isRecord(value))
|
|
3471
3505
|
return "coding_decision";
|
|
3472
3506
|
if (![
|
|
@@ -5203,6 +5237,341 @@ export const RcmRetrievalMetadataSchemaJson = {
|
|
|
5203
5237
|
},
|
|
5204
5238
|
};
|
|
5205
5239
|
// --------------------------------------------------------------------
|
|
5240
|
+
// RcmReviewerEvent (schema 1.0.0, wire rcm-reviewer-event-v1)
|
|
5241
|
+
// --------------------------------------------------------------------
|
|
5242
|
+
export const rcmReviewerEventSchema = z
|
|
5243
|
+
.object({
|
|
5244
|
+
eventName: z.enum([
|
|
5245
|
+
"review_opened",
|
|
5246
|
+
"decision_shown",
|
|
5247
|
+
"evidence_opened",
|
|
5248
|
+
"evidence_copied",
|
|
5249
|
+
"review_active_time",
|
|
5250
|
+
]),
|
|
5251
|
+
occurredAt: z
|
|
5252
|
+
.string()
|
|
5253
|
+
.regex(new RegExp("^\\d{4}-\\d{2}-\\d{2}T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$"))
|
|
5254
|
+
.refine(isRfc3339DateTime)
|
|
5255
|
+
.max(64),
|
|
5256
|
+
tenantRef: z
|
|
5257
|
+
.string()
|
|
5258
|
+
.regex(new RegExp("^[A-Za-z0-9_.:-]+$"))
|
|
5259
|
+
.min(1)
|
|
5260
|
+
.max(128),
|
|
5261
|
+
caseRef: z.string().regex(new RegExp("^[A-Za-z0-9_.:-]+$")).min(1).max(128),
|
|
5262
|
+
reviewId: z
|
|
5263
|
+
.string()
|
|
5264
|
+
.regex(new RegExp("^[A-Za-z0-9_.:-]+$"))
|
|
5265
|
+
.min(1)
|
|
5266
|
+
.max(128)
|
|
5267
|
+
.optional(),
|
|
5268
|
+
workflowId: z.enum([
|
|
5269
|
+
"rcm-coding-assistance",
|
|
5270
|
+
"monthly-pin-time-aggregation",
|
|
5271
|
+
]),
|
|
5272
|
+
packId: z
|
|
5273
|
+
.string()
|
|
5274
|
+
.regex(new RegExp("^[A-Za-z0-9_.:-]+$"))
|
|
5275
|
+
.min(1)
|
|
5276
|
+
.max(80)
|
|
5277
|
+
.optional(),
|
|
5278
|
+
packVersion: z
|
|
5279
|
+
.string()
|
|
5280
|
+
.regex(new RegExp("^[A-Za-z0-9_.:-]+$"))
|
|
5281
|
+
.min(1)
|
|
5282
|
+
.max(80)
|
|
5283
|
+
.optional(),
|
|
5284
|
+
environment: z.enum(["development", "staging", "production"]).optional(),
|
|
5285
|
+
source: z.enum(["portal", "embed"]),
|
|
5286
|
+
evidenceId: z
|
|
5287
|
+
.string()
|
|
5288
|
+
.regex(new RegExp("^[A-Za-z0-9_.:-]{1,64}$"))
|
|
5289
|
+
.min(1)
|
|
5290
|
+
.max(64)
|
|
5291
|
+
.optional(),
|
|
5292
|
+
activeSeconds: z.number().int().min(0).max(86400).optional(),
|
|
5293
|
+
idlePaused: z.boolean().optional(),
|
|
5294
|
+
})
|
|
5295
|
+
.strict()
|
|
5296
|
+
.superRefine((value, ctx) => {
|
|
5297
|
+
const isEvidenceEvent = value["eventName"] === "evidence_opened" ||
|
|
5298
|
+
value["eventName"] === "evidence_copied";
|
|
5299
|
+
const isActiveTimeEvent = value["eventName"] === "review_active_time";
|
|
5300
|
+
if (isEvidenceEvent !== (value.evidenceId !== undefined) ||
|
|
5301
|
+
isActiveTimeEvent !== (value.activeSeconds !== undefined) ||
|
|
5302
|
+
(!isActiveTimeEvent && value.idlePaused !== undefined)) {
|
|
5303
|
+
ctx.addIssue({
|
|
5304
|
+
code: "custom",
|
|
5305
|
+
path: ["eventName"],
|
|
5306
|
+
message: "reviewer_event_shape",
|
|
5307
|
+
});
|
|
5308
|
+
}
|
|
5309
|
+
});
|
|
5310
|
+
export function rcmReviewerEventValidationError(value) {
|
|
5311
|
+
if (!isRecord(value))
|
|
5312
|
+
return "reviewer_event";
|
|
5313
|
+
if (![
|
|
5314
|
+
"eventName",
|
|
5315
|
+
"occurredAt",
|
|
5316
|
+
"tenantRef",
|
|
5317
|
+
"caseRef",
|
|
5318
|
+
"workflowId",
|
|
5319
|
+
"source",
|
|
5320
|
+
].every((key) => key in value))
|
|
5321
|
+
return "reviewer_event_keys";
|
|
5322
|
+
if (!Object.keys(value).every((key) => [
|
|
5323
|
+
"eventName",
|
|
5324
|
+
"occurredAt",
|
|
5325
|
+
"tenantRef",
|
|
5326
|
+
"caseRef",
|
|
5327
|
+
"reviewId",
|
|
5328
|
+
"workflowId",
|
|
5329
|
+
"packId",
|
|
5330
|
+
"packVersion",
|
|
5331
|
+
"environment",
|
|
5332
|
+
"source",
|
|
5333
|
+
"evidenceId",
|
|
5334
|
+
"activeSeconds",
|
|
5335
|
+
"idlePaused",
|
|
5336
|
+
].includes(key)))
|
|
5337
|
+
return "reviewer_event_keys";
|
|
5338
|
+
const value_eventName = value["eventName"];
|
|
5339
|
+
if (typeof value_eventName !== "string" ||
|
|
5340
|
+
![
|
|
5341
|
+
"review_opened",
|
|
5342
|
+
"decision_shown",
|
|
5343
|
+
"evidence_opened",
|
|
5344
|
+
"evidence_copied",
|
|
5345
|
+
"review_active_time",
|
|
5346
|
+
].includes(value_eventName))
|
|
5347
|
+
return "reviewer_event_name";
|
|
5348
|
+
const value_occurredAt = value["occurredAt"];
|
|
5349
|
+
if (typeof value_occurredAt !== "string" ||
|
|
5350
|
+
value_occurredAt.length > 64 ||
|
|
5351
|
+
!new RegExp("^\\d{4}-\\d{2}-\\d{2}T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$").test(value_occurredAt) ||
|
|
5352
|
+
!isRfc3339DateTime(value_occurredAt))
|
|
5353
|
+
return "reviewer_event_occurred_at";
|
|
5354
|
+
const value_tenantRef = value["tenantRef"];
|
|
5355
|
+
if (typeof value_tenantRef !== "string" ||
|
|
5356
|
+
value_tenantRef.length < 1 ||
|
|
5357
|
+
value_tenantRef.length > 128 ||
|
|
5358
|
+
!new RegExp("^[A-Za-z0-9_.:-]+$").test(value_tenantRef))
|
|
5359
|
+
return "reviewer_event_tenant_ref";
|
|
5360
|
+
const value_caseRef = value["caseRef"];
|
|
5361
|
+
if (typeof value_caseRef !== "string" ||
|
|
5362
|
+
value_caseRef.length < 1 ||
|
|
5363
|
+
value_caseRef.length > 128 ||
|
|
5364
|
+
!new RegExp("^[A-Za-z0-9_.:-]+$").test(value_caseRef))
|
|
5365
|
+
return "reviewer_event_case_ref";
|
|
5366
|
+
if (value["reviewId"] !== undefined) {
|
|
5367
|
+
const value_reviewId = value["reviewId"];
|
|
5368
|
+
if (typeof value_reviewId !== "string" ||
|
|
5369
|
+
value_reviewId.length < 1 ||
|
|
5370
|
+
value_reviewId.length > 128 ||
|
|
5371
|
+
!new RegExp("^[A-Za-z0-9_.:-]+$").test(value_reviewId))
|
|
5372
|
+
return "reviewer_event_review_id";
|
|
5373
|
+
}
|
|
5374
|
+
const value_workflowId = value["workflowId"];
|
|
5375
|
+
if (typeof value_workflowId !== "string" ||
|
|
5376
|
+
!["rcm-coding-assistance", "monthly-pin-time-aggregation"].includes(value_workflowId))
|
|
5377
|
+
return "reviewer_event_workflow_id";
|
|
5378
|
+
if (value["packId"] !== undefined) {
|
|
5379
|
+
const value_packId = value["packId"];
|
|
5380
|
+
if (typeof value_packId !== "string" ||
|
|
5381
|
+
value_packId.length < 1 ||
|
|
5382
|
+
value_packId.length > 80 ||
|
|
5383
|
+
!new RegExp("^[A-Za-z0-9_.:-]+$").test(value_packId))
|
|
5384
|
+
return "reviewer_event_pack_id";
|
|
5385
|
+
}
|
|
5386
|
+
if (value["packVersion"] !== undefined) {
|
|
5387
|
+
const value_packVersion = value["packVersion"];
|
|
5388
|
+
if (typeof value_packVersion !== "string" ||
|
|
5389
|
+
value_packVersion.length < 1 ||
|
|
5390
|
+
value_packVersion.length > 80 ||
|
|
5391
|
+
!new RegExp("^[A-Za-z0-9_.:-]+$").test(value_packVersion))
|
|
5392
|
+
return "reviewer_event_pack_version";
|
|
5393
|
+
}
|
|
5394
|
+
if (value["environment"] !== undefined) {
|
|
5395
|
+
const value_environment = value["environment"];
|
|
5396
|
+
if (typeof value_environment !== "string" ||
|
|
5397
|
+
!["development", "staging", "production"].includes(value_environment))
|
|
5398
|
+
return "reviewer_event_environment";
|
|
5399
|
+
}
|
|
5400
|
+
const value_source = value["source"];
|
|
5401
|
+
if (typeof value_source !== "string" ||
|
|
5402
|
+
!["portal", "embed"].includes(value_source))
|
|
5403
|
+
return "reviewer_event_source";
|
|
5404
|
+
if (value["evidenceId"] !== undefined) {
|
|
5405
|
+
const value_evidenceId = value["evidenceId"];
|
|
5406
|
+
if (typeof value_evidenceId !== "string" ||
|
|
5407
|
+
value_evidenceId.length < 1 ||
|
|
5408
|
+
value_evidenceId.length > 64 ||
|
|
5409
|
+
!new RegExp("^[A-Za-z0-9_.:-]{1,64}$").test(value_evidenceId))
|
|
5410
|
+
return "reviewer_event_evidence_id";
|
|
5411
|
+
}
|
|
5412
|
+
if (value["activeSeconds"] !== undefined) {
|
|
5413
|
+
const value_activeSeconds = value["activeSeconds"];
|
|
5414
|
+
if (typeof value_activeSeconds !== "number" ||
|
|
5415
|
+
Number.isNaN(value_activeSeconds) ||
|
|
5416
|
+
!Number.isInteger(value_activeSeconds) ||
|
|
5417
|
+
value_activeSeconds < 0 ||
|
|
5418
|
+
value_activeSeconds > 86400)
|
|
5419
|
+
return "reviewer_event_active_seconds";
|
|
5420
|
+
}
|
|
5421
|
+
if (value["idlePaused"] !== undefined) {
|
|
5422
|
+
const value_idlePaused = value["idlePaused"];
|
|
5423
|
+
if (typeof value_idlePaused !== "boolean")
|
|
5424
|
+
return "reviewer_event_idle_paused";
|
|
5425
|
+
}
|
|
5426
|
+
const eventName = value["eventName"];
|
|
5427
|
+
const isEvidenceEvent = eventName === "evidence_opened" || eventName === "evidence_copied";
|
|
5428
|
+
if (isEvidenceEvent !== (value["evidenceId"] !== undefined))
|
|
5429
|
+
return "reviewer_event_shape";
|
|
5430
|
+
const isActiveTimeEvent = eventName === "review_active_time";
|
|
5431
|
+
if (isActiveTimeEvent !== (value["activeSeconds"] !== undefined))
|
|
5432
|
+
return "reviewer_event_shape";
|
|
5433
|
+
if (!isActiveTimeEvent && value["idlePaused"] !== undefined)
|
|
5434
|
+
return "reviewer_event_shape";
|
|
5435
|
+
return null;
|
|
5436
|
+
}
|
|
5437
|
+
export const validRcmReviewerEvent = (value) => rcmReviewerEventValidationError(value) === null;
|
|
5438
|
+
export const RcmReviewerEventSchemaJson = {
|
|
5439
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
5440
|
+
$id: "https://nexalink.care/contracts/rcm-reviewer-event.schema.json",
|
|
5441
|
+
title: "RcmReviewerEvent",
|
|
5442
|
+
description: "Tenant-scoped, PHI-free reviewer engagement event for CareCompass RCM workflows. Events carry only an opaque case reference, categorical engagement facts, and aggregate durations. They never carry prompts, model responses, document contents, patient or member identifiers, claim identifiers, or free text.",
|
|
5443
|
+
version: "1.0.0",
|
|
5444
|
+
type: "object",
|
|
5445
|
+
additionalProperties: false,
|
|
5446
|
+
required: [
|
|
5447
|
+
"eventName",
|
|
5448
|
+
"occurredAt",
|
|
5449
|
+
"tenantRef",
|
|
5450
|
+
"caseRef",
|
|
5451
|
+
"workflowId",
|
|
5452
|
+
"source",
|
|
5453
|
+
],
|
|
5454
|
+
properties: {
|
|
5455
|
+
eventName: {
|
|
5456
|
+
type: "string",
|
|
5457
|
+
enum: [
|
|
5458
|
+
"review_opened",
|
|
5459
|
+
"decision_shown",
|
|
5460
|
+
"evidence_opened",
|
|
5461
|
+
"evidence_copied",
|
|
5462
|
+
"review_active_time",
|
|
5463
|
+
],
|
|
5464
|
+
},
|
|
5465
|
+
occurredAt: {
|
|
5466
|
+
type: "string",
|
|
5467
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}T(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$",
|
|
5468
|
+
format: "date-time",
|
|
5469
|
+
maxLength: 64,
|
|
5470
|
+
},
|
|
5471
|
+
tenantRef: {
|
|
5472
|
+
type: "string",
|
|
5473
|
+
minLength: 1,
|
|
5474
|
+
maxLength: 128,
|
|
5475
|
+
pattern: "^[A-Za-z0-9_.:-]+$",
|
|
5476
|
+
},
|
|
5477
|
+
caseRef: {
|
|
5478
|
+
type: "string",
|
|
5479
|
+
minLength: 1,
|
|
5480
|
+
maxLength: 128,
|
|
5481
|
+
pattern: "^[A-Za-z0-9_.:-]+$",
|
|
5482
|
+
},
|
|
5483
|
+
reviewId: {
|
|
5484
|
+
type: "string",
|
|
5485
|
+
minLength: 1,
|
|
5486
|
+
maxLength: 128,
|
|
5487
|
+
pattern: "^[A-Za-z0-9_.:-]+$",
|
|
5488
|
+
},
|
|
5489
|
+
workflowId: {
|
|
5490
|
+
type: "string",
|
|
5491
|
+
enum: ["rcm-coding-assistance", "monthly-pin-time-aggregation"],
|
|
5492
|
+
},
|
|
5493
|
+
packId: {
|
|
5494
|
+
type: "string",
|
|
5495
|
+
minLength: 1,
|
|
5496
|
+
maxLength: 80,
|
|
5497
|
+
pattern: "^[A-Za-z0-9_.:-]+$",
|
|
5498
|
+
},
|
|
5499
|
+
packVersion: {
|
|
5500
|
+
type: "string",
|
|
5501
|
+
minLength: 1,
|
|
5502
|
+
maxLength: 80,
|
|
5503
|
+
pattern: "^[A-Za-z0-9_.:-]+$",
|
|
5504
|
+
},
|
|
5505
|
+
environment: {
|
|
5506
|
+
type: "string",
|
|
5507
|
+
enum: ["development", "staging", "production"],
|
|
5508
|
+
},
|
|
5509
|
+
source: {
|
|
5510
|
+
type: "string",
|
|
5511
|
+
enum: ["portal", "embed"],
|
|
5512
|
+
},
|
|
5513
|
+
evidenceId: {
|
|
5514
|
+
type: "string",
|
|
5515
|
+
minLength: 1,
|
|
5516
|
+
pattern: "^[A-Za-z0-9_.:-]{1,64}$",
|
|
5517
|
+
maxLength: 64,
|
|
5518
|
+
},
|
|
5519
|
+
activeSeconds: {
|
|
5520
|
+
type: "integer",
|
|
5521
|
+
minimum: 0,
|
|
5522
|
+
maximum: 86400,
|
|
5523
|
+
},
|
|
5524
|
+
idlePaused: {
|
|
5525
|
+
type: "boolean",
|
|
5526
|
+
},
|
|
5527
|
+
},
|
|
5528
|
+
allOf: [
|
|
5529
|
+
{
|
|
5530
|
+
if: {
|
|
5531
|
+
properties: {
|
|
5532
|
+
eventName: {
|
|
5533
|
+
enum: ["evidence_opened", "evidence_copied"],
|
|
5534
|
+
},
|
|
5535
|
+
},
|
|
5536
|
+
required: ["eventName"],
|
|
5537
|
+
},
|
|
5538
|
+
then: {
|
|
5539
|
+
required: ["evidenceId"],
|
|
5540
|
+
},
|
|
5541
|
+
else: {
|
|
5542
|
+
not: {
|
|
5543
|
+
required: ["evidenceId"],
|
|
5544
|
+
},
|
|
5545
|
+
},
|
|
5546
|
+
},
|
|
5547
|
+
{
|
|
5548
|
+
if: {
|
|
5549
|
+
properties: {
|
|
5550
|
+
eventName: {
|
|
5551
|
+
const: "review_active_time",
|
|
5552
|
+
},
|
|
5553
|
+
},
|
|
5554
|
+
required: ["eventName"],
|
|
5555
|
+
},
|
|
5556
|
+
then: {
|
|
5557
|
+
required: ["activeSeconds"],
|
|
5558
|
+
},
|
|
5559
|
+
else: {
|
|
5560
|
+
not: {
|
|
5561
|
+
anyOf: [
|
|
5562
|
+
{
|
|
5563
|
+
required: ["activeSeconds"],
|
|
5564
|
+
},
|
|
5565
|
+
{
|
|
5566
|
+
required: ["idlePaused"],
|
|
5567
|
+
},
|
|
5568
|
+
],
|
|
5569
|
+
},
|
|
5570
|
+
},
|
|
5571
|
+
},
|
|
5572
|
+
],
|
|
5573
|
+
};
|
|
5574
|
+
// --------------------------------------------------------------------
|
|
5206
5575
|
// RcmRuntimeEvent (schema 1.0.0, wire rcm-runtime-event-v1)
|
|
5207
5576
|
// --------------------------------------------------------------------
|
|
5208
5577
|
export const rcmRuntimeEventSchema = z
|
|
@@ -5560,17 +5929,20 @@ export const RcmRuntimeEventSchemaJson = {
|
|
|
5560
5929
|
},
|
|
5561
5930
|
};
|
|
5562
5931
|
// --------------------------------------------------------------------
|
|
5563
|
-
// RcmWorkflowCatalog (schema 1.
|
|
5932
|
+
// RcmWorkflowCatalog (schema 1.1.0, wire rcm-workflow-catalog-v1)
|
|
5564
5933
|
// --------------------------------------------------------------------
|
|
5565
5934
|
export const rcmWorkflowCatalogSchema = z
|
|
5566
5935
|
.object({
|
|
5567
|
-
id: z.enum(["rcm-coding-assistance"]),
|
|
5936
|
+
id: z.enum(["rcm-coding-assistance", "monthly-pin-time-aggregation"]),
|
|
5568
5937
|
name: z.string().min(1).max(120),
|
|
5569
5938
|
description: z.string().min(1).max(400),
|
|
5570
5939
|
environments: z.array(z.enum(["development", "production"])).min(1),
|
|
5571
5940
|
modelPolicy: z
|
|
5572
5941
|
.object({
|
|
5573
|
-
policyId: z.
|
|
5942
|
+
policyId: z.enum([
|
|
5943
|
+
"carecompass-rcm-review-v1",
|
|
5944
|
+
"carecompass-pin-time-policy-v1",
|
|
5945
|
+
]),
|
|
5574
5946
|
inferencePath: z.string().max(64),
|
|
5575
5947
|
maxNewTokens: z.number().int().min(1).max(2048),
|
|
5576
5948
|
temperature: z.number().min(0).max(2),
|
|
@@ -5586,7 +5958,21 @@ export const rcmWorkflowCatalogSchema = z
|
|
|
5586
5958
|
.strict(),
|
|
5587
5959
|
outputSchemaVersion: z.literal("rcm-grounded-human-review-v2"),
|
|
5588
5960
|
})
|
|
5589
|
-
.strict()
|
|
5961
|
+
.strict()
|
|
5962
|
+
.superRefine((value, ctx) => {
|
|
5963
|
+
const expectedPolicy = value.id === "rcm-coding-assistance"
|
|
5964
|
+
? "carecompass-rcm-review-v1"
|
|
5965
|
+
: "carecompass-pin-time-policy-v1";
|
|
5966
|
+
if (value.modelPolicy.policyId !== expectedPolicy ||
|
|
5967
|
+
(value.id === "monthly-pin-time-aggregation" &&
|
|
5968
|
+
value.environments.some((environment) => environment !== "development"))) {
|
|
5969
|
+
ctx.addIssue({
|
|
5970
|
+
code: "custom",
|
|
5971
|
+
path: ["modelPolicy"],
|
|
5972
|
+
message: "workflow_policy_binding",
|
|
5973
|
+
});
|
|
5974
|
+
}
|
|
5975
|
+
});
|
|
5590
5976
|
export function rcmWorkflowCatalogValidationError(value) {
|
|
5591
5977
|
if (!isRecord(value))
|
|
5592
5978
|
return "workflow_catalog";
|
|
@@ -5612,7 +5998,7 @@ export function rcmWorkflowCatalogValidationError(value) {
|
|
|
5612
5998
|
return "workflow_catalog_keys";
|
|
5613
5999
|
const value_id = value["id"];
|
|
5614
6000
|
if (typeof value_id !== "string" ||
|
|
5615
|
-
!["rcm-coding-assistance"].includes(value_id))
|
|
6001
|
+
!["rcm-coding-assistance", "monthly-pin-time-aggregation"].includes(value_id))
|
|
5616
6002
|
return "workflow_id";
|
|
5617
6003
|
const value_name = value["name"];
|
|
5618
6004
|
if (typeof value_name !== "string" ||
|
|
@@ -5655,7 +6041,7 @@ export function rcmWorkflowCatalogValidationError(value) {
|
|
|
5655
6041
|
return "model_policy_keys";
|
|
5656
6042
|
const value_modelPolicy_policyId = value_modelPolicy["policyId"];
|
|
5657
6043
|
if (typeof value_modelPolicy_policyId !== "string" ||
|
|
5658
|
-
|
|
6044
|
+
!["carecompass-rcm-review-v1", "carecompass-pin-time-policy-v1"].includes(value_modelPolicy_policyId))
|
|
5659
6045
|
return "model_policy_id";
|
|
5660
6046
|
const value_modelPolicy_inferencePath = value_modelPolicy["inferencePath"];
|
|
5661
6047
|
if (typeof value_modelPolicy_inferencePath !== "string" ||
|
|
@@ -5701,6 +6087,15 @@ export function rcmWorkflowCatalogValidationError(value) {
|
|
|
5701
6087
|
if (typeof value_outputSchemaVersion !== "string" ||
|
|
5702
6088
|
value_outputSchemaVersion !== "rcm-grounded-human-review-v2")
|
|
5703
6089
|
return "workflow_output_schema_version";
|
|
6090
|
+
const policy = isRecord(value["modelPolicy"]) ? value["modelPolicy"] : {};
|
|
6091
|
+
const expectedPolicy = value["id"] === "rcm-coding-assistance"
|
|
6092
|
+
? "carecompass-rcm-review-v1"
|
|
6093
|
+
: "carecompass-pin-time-policy-v1";
|
|
6094
|
+
if (policy["policyId"] !== expectedPolicy ||
|
|
6095
|
+
(value["id"] === "monthly-pin-time-aggregation" &&
|
|
6096
|
+
Array.isArray(value["environments"]) &&
|
|
6097
|
+
value["environments"].some((environment) => environment !== "development")))
|
|
6098
|
+
return "workflow_policy_binding";
|
|
5704
6099
|
return null;
|
|
5705
6100
|
}
|
|
5706
6101
|
export const validRcmWorkflowCatalog = (value) => rcmWorkflowCatalogValidationError(value) === null;
|
|
@@ -5708,8 +6103,8 @@ export const RcmWorkflowCatalogSchemaJson = {
|
|
|
5708
6103
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
5709
6104
|
$id: "https://nexalink.care/contracts/rcm-workflow-catalog.schema.json",
|
|
5710
6105
|
title: "RcmWorkflowCatalog",
|
|
5711
|
-
description: "A single authorized workflow entry. The catalog pins the workflow identifier, model policy, human-review policy, and output schema version that the runtime and portal may offer for
|
|
5712
|
-
version: "1.
|
|
6106
|
+
description: "A single authorized workflow entry. The catalog pins the workflow identifier, model policy, human-review policy, and output schema version that the runtime and portal may offer for registered coding workflows (currently rcm-coding-assistance and monthly-pin-time-aggregation). Customer configuration can never redefine these immutable server-side values.",
|
|
6107
|
+
version: "1.1.0",
|
|
5713
6108
|
type: "object",
|
|
5714
6109
|
additionalProperties: false,
|
|
5715
6110
|
required: [
|
|
@@ -5724,7 +6119,7 @@ export const RcmWorkflowCatalogSchemaJson = {
|
|
|
5724
6119
|
properties: {
|
|
5725
6120
|
id: {
|
|
5726
6121
|
type: "string",
|
|
5727
|
-
enum: ["rcm-coding-assistance"],
|
|
6122
|
+
enum: ["rcm-coding-assistance", "monthly-pin-time-aggregation"],
|
|
5728
6123
|
},
|
|
5729
6124
|
name: {
|
|
5730
6125
|
type: "string",
|
|
@@ -5758,7 +6153,7 @@ export const RcmWorkflowCatalogSchemaJson = {
|
|
|
5758
6153
|
properties: {
|
|
5759
6154
|
policyId: {
|
|
5760
6155
|
type: "string",
|
|
5761
|
-
|
|
6156
|
+
enum: ["carecompass-rcm-review-v1", "carecompass-pin-time-policy-v1"],
|
|
5762
6157
|
},
|
|
5763
6158
|
inferencePath: {
|
|
5764
6159
|
type: "string",
|
|
@@ -5804,4 +6199,51 @@ export const RcmWorkflowCatalogSchemaJson = {
|
|
|
5804
6199
|
const: "rcm-grounded-human-review-v2",
|
|
5805
6200
|
},
|
|
5806
6201
|
},
|
|
6202
|
+
allOf: [
|
|
6203
|
+
{
|
|
6204
|
+
if: {
|
|
6205
|
+
properties: {
|
|
6206
|
+
id: {
|
|
6207
|
+
const: "rcm-coding-assistance",
|
|
6208
|
+
},
|
|
6209
|
+
},
|
|
6210
|
+
},
|
|
6211
|
+
then: {
|
|
6212
|
+
properties: {
|
|
6213
|
+
modelPolicy: {
|
|
6214
|
+
properties: {
|
|
6215
|
+
policyId: {
|
|
6216
|
+
const: "carecompass-rcm-review-v1",
|
|
6217
|
+
},
|
|
6218
|
+
},
|
|
6219
|
+
},
|
|
6220
|
+
},
|
|
6221
|
+
},
|
|
6222
|
+
},
|
|
6223
|
+
{
|
|
6224
|
+
if: {
|
|
6225
|
+
properties: {
|
|
6226
|
+
id: {
|
|
6227
|
+
const: "monthly-pin-time-aggregation",
|
|
6228
|
+
},
|
|
6229
|
+
},
|
|
6230
|
+
},
|
|
6231
|
+
then: {
|
|
6232
|
+
properties: {
|
|
6233
|
+
modelPolicy: {
|
|
6234
|
+
properties: {
|
|
6235
|
+
policyId: {
|
|
6236
|
+
const: "carecompass-pin-time-policy-v1",
|
|
6237
|
+
},
|
|
6238
|
+
},
|
|
6239
|
+
},
|
|
6240
|
+
environments: {
|
|
6241
|
+
items: {
|
|
6242
|
+
const: "development",
|
|
6243
|
+
},
|
|
6244
|
+
},
|
|
6245
|
+
},
|
|
6246
|
+
},
|
|
6247
|
+
},
|
|
6248
|
+
],
|
|
5807
6249
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexalink/coding-contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Versioned TypeScript types, validators, constants, and JSON Schemas for CareCompass coding workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -32,8 +32,10 @@
|
|
|
32
32
|
"contracts:check": "bun scripts/codegen.ts --check",
|
|
33
33
|
"format": "bunx prettier --write --ignore-unknown .",
|
|
34
34
|
"format:check": "bunx prettier --check --ignore-unknown .",
|
|
35
|
+
"lint": "bunx eslint .",
|
|
35
36
|
"test": "bun test",
|
|
36
37
|
"test:python": "PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=python .venv/bin/python -m pytest -p no:cacheprovider python/tests",
|
|
38
|
+
"tidy": "bun run format && bun run lint",
|
|
37
39
|
"audit": "bun audit",
|
|
38
40
|
"verify:package": "bun scripts/verify-package.mjs",
|
|
39
41
|
"pack:dry-run": "bun run build && bun run verify:package && bun pm pack --dry-run --ignore-scripts",
|
|
@@ -43,8 +45,12 @@
|
|
|
43
45
|
"zod": "^4.4.3"
|
|
44
46
|
},
|
|
45
47
|
"devDependencies": {
|
|
48
|
+
"@eslint/js": "^10.0.1",
|
|
49
|
+
"eslint": "^10.12.0",
|
|
50
|
+
"globals": "^17.13.0",
|
|
46
51
|
"prettier": "3.8.1",
|
|
47
|
-
"typescript": "^5.9.2"
|
|
52
|
+
"typescript": "^5.9.2",
|
|
53
|
+
"typescript-eslint": "^8.71.0"
|
|
48
54
|
},
|
|
49
55
|
"publishConfig": {
|
|
50
56
|
"access": "public",
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://nexalink.care/contracts/coding-reviewer-action.schema.json",
|
|
4
4
|
"title": "CodingReviewerAction",
|
|
5
|
-
"description": "Attributable human-review disposition appended to an otherwise immutable decision. A decision plus its reviewer actions remains replayable: original and reviewer versions are both preserved.",
|
|
6
|
-
"version": "1.
|
|
5
|
+
"description": "Attributable human-review disposition appended to an otherwise immutable decision. A decision plus its reviewer actions remains replayable: original and reviewer versions are both preserved. Review outcome categories are reviewer-adjudicated observations and never inferred from payment, code, or reimbursement changes.",
|
|
6
|
+
"version": "1.1.0",
|
|
7
7
|
"x-wire-version": "coding-reviewer-action-v1",
|
|
8
8
|
"x-wire-constant": "CODING_REVIEWER_ACTION_SCHEMA_VERSION",
|
|
9
9
|
"x-code": "reviewer_action",
|
|
@@ -35,6 +35,17 @@
|
|
|
35
35
|
"maxLength": 80,
|
|
36
36
|
"x-code": "reviewer_action_resolution_code"
|
|
37
37
|
},
|
|
38
|
+
"reviewOutcomeCategory": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"enum": [
|
|
41
|
+
"unsupported_or_excessive",
|
|
42
|
+
"missed_supported",
|
|
43
|
+
"unchanged_supported",
|
|
44
|
+
"missing_evidence",
|
|
45
|
+
"escalation"
|
|
46
|
+
],
|
|
47
|
+
"x-code": "reviewer_action_outcome_category"
|
|
48
|
+
},
|
|
38
49
|
"notes": {
|
|
39
50
|
"type": "string",
|
|
40
51
|
"minLength": 1,
|