Merge remote-tracking branch 'origin/main' into feat/document-ai-runs

# Conflicts:
#	docs/api/openapi.yaml
This commit is contained in:
Marc-André Bombeck committed 2026-10-03 18:41:55 +02:00
commit 20470d99f4
28 files changed
+891 -38

No files matched your search

+1
View File
@@ -218,6 +218,7 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
"labs.ocr.rateLimited",
"labs.restore.invalid",
"labs.result.notFound",
"labs.unit.mismatch",
"labs.update.invalid",
"labs.update.linkedFieldsImmutable",
"labs.update.numericExpected",
+5
View File
@@ -162,6 +162,11 @@ export const labsPaths: NonNullable<ZodOpenApiObject["paths"]> = {
},
},
...stdResponses,
"422": {
description:
"Nothing was written. `meta.errorCode` = `labs.create.invalid` when the body failed validation (every issue under `details.issues`), or `labs.unit.mismatch` when a numeric reading states a `unit` other than the one its marker is tracked in (`meta.markerUnit`, `meta.readingUnit`). A unit is compared only when one is sent; with a `biomarkerId` it may be omitted and the marker's unit is then the stated one. Spelling is forgiven (`mmol/l`, `ug/L` for `µg/L`); a different unit is refused, never relabelled.",
content: { "application/json": { schema: errorEnvelope } },
},
...recordWriteRateLimitResponse,
},
},
+10 -3
View File
@@ -73,6 +73,10 @@ const extractedRow = z
takenAt: z.string().nullable(),
confidence: extractConfidence,
biomarkerMatch: z.enum(["new", "existing"]),
markerUnit: z.string().nullable().meta({
description:
"The unit the matched marker is tracked in, or null when the marker does not exist yet (it will adopt this row's unit). A row whose `unit` differs from it cannot be committed: the commit skips it with `reason: unit_mismatch`, and the review screen marks it before Save.",
}),
duplicateOf: z.string().nullable(),
})
.meta({
@@ -139,7 +143,10 @@ const commitResponse = z
skipped: z.array(
z.object({
analyte: z.string(),
reason: z.literal("duplicate"),
reason: z.enum(["duplicate", "unit_mismatch"]).meta({
description:
"`duplicate`: the row matched a live reading at commit time. `unit_mismatch`: the row states a unit other than the one its marker is tracked in, so it was not written.",
}),
}),
),
outcome: z.enum(["empty", "failed", "partial", "success"]).meta({
@@ -150,7 +157,7 @@ const commitResponse = z
.meta({
id: "OcrCommitResponse",
description:
'The rows written (`source: "OCR"`), those skipped as commit-time duplicates, and the resolved outcome.',
'The rows written (`source: "OCR"`), those skipped (commit-time duplicates, or a unit that differs from that of the marker), and the resolved outcome.',
});
export const ocrPaths: NonNullable<ZodOpenApiObject["paths"]> = {
@@ -217,7 +224,7 @@ export const ocrPaths: NonNullable<ZodOpenApiObject["paths"]> = {
tags: ["Labs"],
summary: "Commit confirmed Lab-OCR rows",
description:
'Writes ONLY the rows the human confirmed on the review screen. Each row resolves-or-mints a user-scoped biomarker and creates a `LabResult` with `source: "OCR"`; a row that duplicates a live reading is skipped. Idempotent (Idempotency-Key). Audits as `labs.ocr.commit`. `userId` is narrowed from the session, never a body field.',
'Writes ONLY the rows the human confirmed on the review screen. Each row resolves-or-mints a user-scoped biomarker and creates a `LabResult` with `source: "OCR"`; a row that duplicates a live reading, or states a unit other than that of its marker, is skipped (`skipped[].reason`). Idempotent (Idempotency-Key). Audits as `labs.ocr.commit`. `userId` is narrowed from the session, never a body field.',
requestBody: {
required: true,
content: { "application/json": { schema: ocrCommitRequest } },