///|
/// Project-specific whole-field expectations over effective inherited metadata.
/// Optional numeric rules add exact decimal policy, without unit conversion.
pub struct MetadataExpectations {
priv required : Array[String]
priv allowed : Map[String, Array[Json]]
priv fields : Array[String]
priv numeric : NumericRules?
}
///|
fn MetadataExpectations::expectations_value(
self : MetadataExpectations,
) -> Json {
let allowed : Map[String, Json] = Map([])
for field in self.fields {
if self.allowed.get(field) is Some(values) {
allowed[field] = Json::array(values)
}
}
let document : Map[String, Json] = {
"required": Json::array(self.required.map(Json::string)),
"allowed": Json::object(allowed),
}
if self.numeric is Some(rules) {
document["numeric"] = rules.rules_value()
}
Json::object(document)
}
///|
pub fn MetadataExpectations::to_json(self : MetadataExpectations) -> String {
canonical_json(self.expectations_value())
}
///|
/// Strict required/allowed controls, with optional numeric rules.
/// Allowed-only fields may be absent; explicit null is a present value.
pub fn MetadataExpectations::from_json(
text : String,
) -> MetadataExpectations raise TraceError {
guard parse_source("", text, container_limit=67)
is Object(input) else {
reject(
"invalid_expectations", "", "Expected required and allowed controls.",
)
abort("reject raises")
}
for key in sorted_keys(input) {
if !["required", "allowed", "numeric"].contains(key) {
reject(
"unknown_expectation_field", key, "Use only required, allowed and optional numeric controls.",
)
}
}
guard input.get("required") is Some(Array(values)) &&
input.get("allowed") is Some(Object(choices)) else {
reject(
"invalid_expectations", "", "Both required array and allowed object are mandatory.",
)
abort("reject raises")
}
let required = values.map(value => {
guard value is String(field) else {
reject(
"invalid_expectations", "/required", "Required entries must be field names.",
)
abort("reject raises")
}
field
})
ignore(review_fields(required))
required.sort_by(lexical_compare)
let all : Map[String, Bool] = Map([])
for field in required {
all[field] = true
}
let allowed : Map[String, Array[Json]] = Map([])
for field in sorted_keys(choices) {
all[field] = true
guard choices[field] is Array(items) &&
!items.is_empty() &&
items.length() <= 256 else {
reject(
"invalid_allowed_values", field, "Allowed values must be a nonempty array with at most 256 exact choices.",
)
abort("reject raises")
}
let tokens : Map[String, Json] = Map([])
for item in items {
let token = canonical_json(item)
if tokens.contains(token) {
reject(
"duplicate_allowed_value", field, "Allowed value occurs more than once; remove the duplicate choice.",
)
}
tokens[token] = item
}
allowed[field] = sorted_keys(tokens).map(token => tokens[token])
}
let fields = review_fields(sorted_keys(all))
let numeric = input
.get("numeric")
.map(value => NumericRules::from_json(canonical_json(value)))
if numeric is Some(rules) {
for rule in rules.fields {
all[rule.field] = true
}
ignore(review_fields(sorted_keys(all)))
}
let expectations : MetadataExpectations = {
required,
allowed,
fields,
numeric,
}
if expectations.to_json().length() > 2097152 {
reject(
"input_limit", "", "Canonical expectations exceed 2,097,152 UTF-16 units; reduce the review scope.",
)
}
expectations
}
///|
/// Attach/replace numeric policy, retaining existing exact-value expectations.
pub fn MetadataExpectations::with_numeric(
self : MetadataExpectations,
rules : NumericRules,
) -> MetadataExpectations raise TraceError {
guard self.expectations_value() is Object(fields) else {
abort("object invariant")
}
fields["numeric"] = rules.rules_value()
MetadataExpectations::from_json(canonical_json(Json::object(fields)))
}
///|
pub(all) enum ExpectationDecision {
Pass
Violations
Indeterminate
} derive(Eq)
///|
pub extend ExpectationDecision with Eq::{equal, not_equal}
///|
pub struct ExpectationReport {
priv outcome : ExpectationDecision
priv violations : Int32
priv errors : Int32
priv document : Json
}
///|
pub fn ExpectationReport::decision(
self : ExpectationReport,
) -> ExpectationDecision {
self.outcome
}
///|
/// Counts failed legacy fields plus numeric fields/relations, including repeats.
pub fn ExpectationReport::violation_count(self : ExpectationReport) -> Int32 {
self.violations
}
///|
pub fn ExpectationReport::error_count(self : ExpectationReport) -> Int32 {
self.errors
}
///|
pub fn ExpectationReport::to_json(self : ExpectationReport) -> String {
canonical_json(self.document)
}
///|
pub fn ExpectationReport::to_json_value(self : ExpectationReport) -> Json {
copy_json(self.document)
}
///|
/// Errors take priority. An empty audit is indeterminate, never vacuous approval.
/// Every requested occurrence retains complete effective/source/error evidence.
pub fn AuditReport::check_expectations(
self : AuditReport,
expectations : MetadataExpectations,
) -> ExpectationReport {
let mut violations = 0
let mut errors = 0
let entries = self.entries.mapi((i, entry) => {
let document : Map[String, Json] = {
"query_index": Json::number(i.to_double()),
"data_path": Json::string(entry.path),
}
match (entry.result, entry.error) {
(None, Some(error)) => {
errors += 1
document["status"] = Json::string("error")
document["diagnostic"] = error.to_json()
}
(Some(result), None) => {
let checks = expectations.fields.map(field => {
let required = expectations.required.contains(field)
let choices = expectations.allowed.get(field)
let actual = result.values.get(field)
let check : Map[String, Json] = {
"field": Json::string(field),
"required": Json::boolean(required),
"present": Json::boolean(actual is Some(_)),
"allowed": match choices {
Some(values) => Json::array(values)
None => Json::null()
},
}
let status = match actual {
None =>
if required {
"missing_required"
} else {
"absent_optional"
}
Some(value) => {
check["actual"] = value
let history = result.origins[field]
check["trace"] = history_value(history)
check["winning_source"] = Json::string(
history[history.length() - 1].path,
)
match choices {
Some(values) =>
if values.any(v => canonical_json(v) == canonical_json(value)) {
"pass"
} else {
"unexpected_value"
}
None => "pass"
}
}
}
if status == "missing_required" || status == "unexpected_value" {
violations += 1
}
check["status"] = Json::string(status)
Json::object(check)
})
document["status"] = Json::string("resolved")
document["result"] = result.report_value()
document["checks"] = Json::array(checks)
if expectations.numeric is Some(rules) {
let (numeric_checks, failed) = rules.evaluate(result)
violations += failed
document["numeric_checks"] = Json::array(numeric_checks)
}
}
_ => abort("AuditEntry invariant")
}
Json::object(document)
})
let outcome : ExpectationDecision = if errors > 0 || entries.is_empty() {
Indeterminate
} else if violations > 0 {
Violations
} else {
Pass
}
let decision = match outcome {
Pass => "pass"
Violations => "violations"
Indeterminate => "indeterminate"
}
{
outcome,
violations,
errors,
document: Json::object({
"decision": Json::string(decision),
"empty_audit": Json::boolean(entries.is_empty()),
"violation_count": Json::number(violations.to_double()),
"error_count": Json::number(errors.to_double()),
"expectations": expectations.expectations_value(),
"entries": Json::array(entries),
}),
}
}