let _adr_id_format = std.contract.custom ( fun label => fun value => if std.string.is_match "^adr-[0-9]{3}$" value then 'Ok value else 'Error { message = "ADR id must match 'adr-NNN' format (e.g. 'adr-001'), got: '%{value}'" } ) in let _non_empty_constraints = std.contract.custom ( fun label => fun value => if std.array.length value == 0 then 'Error { message = "constraints must not be empty — an ADR with no constraints is passive documentation, not an active constraint" } else 'Ok value ) in let _non_empty_negative = std.contract.custom ( fun label => fun value => if std.array.length value.negative == 0 then 'Error { message = "consequences.negative must not be empty on id='%{value.id}' — an ADR with no negative consequences is incomplete" } else 'Ok value ) in let _requires_justification = std.contract.custom ( fun label => fun value => if value.ontology_check.verdict == 'RequiresJustification && !(std.record.has_field "invariant_justification" value) then 'Error { message = "ADR '%{value.id}': ontology_check.verdict = 'RequiresJustification but invariant_justification field is missing" } else 'Ok value ) in { AdrIdFormat = _adr_id_format, NonEmptyConstraints = _non_empty_constraints, NonEmptyNegativeConsequences = _non_empty_negative, RequiresJustificationWhenRisky = _requires_justification, }