commit 9a3821f8bf68d037d5118af975ed61169230b5a2
parent 6fecd18e466a54000098f2fe7b121d547c4a801b
Author: triesap <tyson@radroots.org>
Date: Mon, 21 Sep 2026 03:38:01 +0000
spec: bind typed outcome and reason taxonomy
Diffstat:
10 files changed, 434 insertions(+), 0 deletions(-)
diff --git a/schemas/hyf_v1_jev/examples/invalid/outcome.bad_eligibility.json b/schemas/hyf_v1_jev/examples/invalid/outcome.bad_eligibility.json
@@ -0,0 +1,12 @@
+{
+ "eligibility": "maybe",
+ "review": {
+ "required": false,
+ "clarifications": []
+ },
+ "checks": [],
+ "execution": {
+ "status": "complete",
+ "provider_calls": 0
+ }
+}
diff --git a/schemas/hyf_v1_jev/examples/invalid/outcome.bad_reason.json b/schemas/hyf_v1_jev/examples/invalid/outcome.bad_reason.json
@@ -0,0 +1,19 @@
+{
+ "eligibility": "eligible",
+ "review": {
+ "required": false,
+ "clarifications": []
+ },
+ "checks": [
+ {
+ "kind": "product",
+ "result": "pass",
+ "reason": "looks_good",
+ "mandatory": true
+ }
+ ],
+ "execution": {
+ "status": "complete",
+ "provider_calls": 0
+ }
+}
diff --git a/schemas/hyf_v1_jev/examples/invalid/outcome.contradictory_eligible_with_fail.json b/schemas/hyf_v1_jev/examples/invalid/outcome.contradictory_eligible_with_fail.json
@@ -0,0 +1,19 @@
+{
+ "eligibility": "eligible",
+ "review": {
+ "required": false,
+ "clarifications": []
+ },
+ "checks": [
+ {
+ "kind": "quantity",
+ "result": "fail",
+ "reason": "quantity_insufficient",
+ "mandatory": true
+ }
+ ],
+ "execution": {
+ "status": "complete",
+ "provider_calls": 0
+ }
+}
diff --git a/schemas/hyf_v1_jev/examples/valid/outcome.conditional_uncertain.json b/schemas/hyf_v1_jev/examples/valid/outcome.conditional_uncertain.json
@@ -0,0 +1,30 @@
+{
+ "eligibility": "conditional",
+ "review": {
+ "required": true,
+ "clarifications": [
+ {
+ "field": "unreserved_quantity",
+ "reason": "quantity_unknown"
+ }
+ ]
+ },
+ "checks": [
+ {
+ "kind": "product",
+ "result": "pass",
+ "reason": "product_mismatch",
+ "mandatory": true
+ },
+ {
+ "kind": "quantity",
+ "result": "unknown",
+ "reason": "stock_unknown",
+ "mandatory": true
+ }
+ ],
+ "execution": {
+ "status": "complete",
+ "provider_calls": 0
+ }
+}
diff --git a/schemas/hyf_v1_jev/examples/valid/outcome.degraded_optional.json b/schemas/hyf_v1_jev/examples/valid/outcome.degraded_optional.json
@@ -0,0 +1,26 @@
+{
+ "eligibility": "eligible",
+ "review": {
+ "required": false,
+ "clarifications": []
+ },
+ "checks": [
+ {
+ "kind": "product",
+ "result": "pass",
+ "reason": "product_mismatch",
+ "mandatory": true
+ },
+ {
+ "kind": "preference",
+ "result": "unknown",
+ "reason": "evidence_insufficient",
+ "mandatory": false
+ }
+ ],
+ "execution": {
+ "status": "degraded",
+ "reason": "provider_degraded",
+ "provider_calls": 0
+ }
+}
diff --git a/schemas/hyf_v1_jev/examples/valid/outcome.ineligible_fail.json b/schemas/hyf_v1_jev/examples/valid/outcome.ineligible_fail.json
@@ -0,0 +1,25 @@
+{
+ "eligibility": "ineligible",
+ "review": {
+ "required": false,
+ "clarifications": []
+ },
+ "checks": [
+ {
+ "kind": "quantity",
+ "result": "fail",
+ "reason": "quantity_insufficient",
+ "mandatory": true
+ },
+ {
+ "kind": "window",
+ "result": "unknown",
+ "reason": "window_mismatch",
+ "mandatory": true
+ }
+ ],
+ "execution": {
+ "status": "complete",
+ "provider_calls": 0
+ }
+}
diff --git a/schemas/hyf_v1_jev/outcome.schema.json b/schemas/hyf_v1_jev/outcome.schema.json
@@ -0,0 +1,200 @@
+{
+ "$comment": "Typed business outcome; eligibility is separate from execution status; no contradictory combinations",
+ "type": "object",
+ "properties": {
+ "eligibility": {
+ "type": "string",
+ "enum": [
+ "eligible",
+ "conditional",
+ "ineligible"
+ ]
+ },
+ "review": {
+ "type": "object",
+ "properties": {
+ "required": {
+ "type": "boolean"
+ },
+ "clarifications": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "field": {
+ "type": "string"
+ },
+ "reason": {
+ "type": "string",
+ "enum": [
+ "quantity_insufficient",
+ "product_mismatch",
+ "substitution_not_permitted",
+ "fulfillment_mismatch",
+ "area_unknown",
+ "window_mismatch",
+ "price_above_ceiling",
+ "attribute_unverified",
+ "quantity_unknown",
+ "information_needed",
+ "review_required",
+ "evidence_insufficient",
+ "stock_unknown",
+ "provider_unavailable",
+ "provider_degraded",
+ "budget_exhausted"
+ ]
+ }
+ },
+ "required": [
+ "field",
+ "reason"
+ ],
+ "additionalProperties": false
+ }
+ }
+ },
+ "required": [
+ "required",
+ "clarifications"
+ ],
+ "additionalProperties": false
+ },
+ "checks": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "kind": {
+ "type": "string"
+ },
+ "applicable": {
+ "type": "boolean"
+ },
+ "result": {
+ "type": "string",
+ "enum": [
+ "pass",
+ "fail",
+ "unknown"
+ ]
+ },
+ "reason": {
+ "type": "string",
+ "enum": [
+ "quantity_insufficient",
+ "product_mismatch",
+ "substitution_not_permitted",
+ "fulfillment_mismatch",
+ "area_unknown",
+ "window_mismatch",
+ "price_above_ceiling",
+ "attribute_unverified",
+ "quantity_unknown",
+ "information_needed",
+ "review_required",
+ "evidence_insufficient",
+ "stock_unknown",
+ "provider_unavailable",
+ "provider_degraded",
+ "budget_exhausted"
+ ]
+ },
+ "mandatory": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "kind",
+ "result",
+ "reason",
+ "mandatory"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "execution": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string",
+ "enum": [
+ "complete",
+ "degraded",
+ "failed"
+ ]
+ },
+ "reason": {
+ "type": "string",
+ "enum": [
+ "quantity_insufficient",
+ "product_mismatch",
+ "substitution_not_permitted",
+ "fulfillment_mismatch",
+ "area_unknown",
+ "window_mismatch",
+ "price_above_ceiling",
+ "attribute_unverified",
+ "quantity_unknown",
+ "information_needed",
+ "review_required",
+ "evidence_insufficient",
+ "stock_unknown",
+ "provider_unavailable",
+ "provider_degraded",
+ "budget_exhausted"
+ ]
+ },
+ "provider_calls": {
+ "type": "integer",
+ "minimum": 0
+ }
+ },
+ "required": [
+ "status",
+ "provider_calls"
+ ],
+ "additionalProperties": false
+ },
+ "error": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "properties": {
+ "family": {
+ "type": "string",
+ "enum": [
+ "invalid_request",
+ "unsupported_capability",
+ "capability_disabled",
+ "capability_unavailable",
+ "invalid_context",
+ "budget_exhausted",
+ "provider_auth",
+ "provider_validation",
+ "provider_capacity",
+ "provider_transport",
+ "provider_response_contract",
+ "internal_error"
+ ]
+ },
+ "message": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "family",
+ "message"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "required": [
+ "eligibility",
+ "review",
+ "checks",
+ "execution"
+ ],
+ "additionalProperties": false
+}
diff --git a/schemas/hyf_v1_jev/outcome_manifest.json b/schemas/hyf_v1_jev/outcome_manifest.json
@@ -0,0 +1,22 @@
+{
+ "schema_version": 1,
+ "spec_id": "hyf_v1_jev",
+ "family": "outcomes",
+ "bindings": [
+ {
+ "schema": "outcome.schema.json",
+ "valid": [
+ "examples/valid/outcome.conditional_uncertain.json",
+ "examples/valid/outcome.ineligible_fail.json",
+ "examples/valid/outcome.degraded_optional.json"
+ ],
+ "invalid": [
+ "examples/invalid/outcome.bad_eligibility.json",
+ "examples/invalid/outcome.bad_reason.json"
+ ],
+ "semantic_invalid": [
+ "examples/invalid/outcome.contradictory_eligible_with_fail.json"
+ ]
+ }
+ ]
+}
+\ No newline at end of file
diff --git a/schemas/hyf_v1_jev/reason.schema.json b/schemas/hyf_v1_jev/reason.schema.json
@@ -0,0 +1,22 @@
+{
+ "$comment": "stable reason-code inventory; consumers branch on codes, not substrings",
+ "type": "string",
+ "enum": [
+ "quantity_insufficient",
+ "product_mismatch",
+ "substitution_not_permitted",
+ "fulfillment_mismatch",
+ "area_unknown",
+ "window_mismatch",
+ "price_above_ceiling",
+ "attribute_unverified",
+ "quantity_unknown",
+ "information_needed",
+ "review_required",
+ "evidence_insufficient",
+ "stock_unknown",
+ "provider_unavailable",
+ "provider_degraded",
+ "budget_exhausted"
+ ]
+}
diff --git a/tests/test_hyf.mojo b/tests/test_hyf.mojo
@@ -1111,5 +1111,63 @@ def test_temporal_evidence_schemas_accept_valid_and_reject_invalid() raises:
_assert_manifest_examples("temporal_manifest.json", 7)
+def _outcome_is_contradictory(doc: Value) raises -> Bool:
+ var eligibility = doc["eligibility"].string_value()
+ var mandatory_fail = False
+ var mandatory_unknown = False
+ for check in doc["checks"].array_items():
+ var result = check["result"].string_value()
+ if check["mandatory"].bool_value():
+ if result == "fail":
+ mandatory_fail = True
+ elif result == "unknown":
+ mandatory_unknown = True
+ if mandatory_fail and eligibility != "ineligible":
+ return True
+ if not mandatory_fail and mandatory_unknown and eligibility == "eligible":
+ return True
+ return False
+
+
+def test_outcome_composition_rejects_contradictions() raises:
+ _assert_manifest_examples("outcome_manifest.json", 3)
+ var manifest = _wire_schema_json("outcome_manifest.json")
+ for binding in manifest["bindings"].array_items():
+ var schema = _wire_schema_json(binding["schema"].string_value())
+ for rel in binding["valid"].array_items():
+ var doc = loads(
+ (
+ _dir_of_current_file()
+ / ".."
+ / "schemas"
+ / "hyf_v1_jev"
+ / rel.string_value()
+ ).read_text()
+ )
+ assert_true(validate(doc, schema).valid)
+ assert_true(
+ not _outcome_is_contradictory(doc),
+ "valid outcome treated as contradictory: " + rel.string_value(),
+ )
+ for rel in binding["semantic_invalid"].array_items():
+ var doc = loads(
+ (
+ _dir_of_current_file()
+ / ".."
+ / "schemas"
+ / "hyf_v1_jev"
+ / rel.string_value()
+ ).read_text()
+ )
+ assert_true(
+ validate(doc, schema).valid,
+ "semantic-invalid example must remain structurally valid",
+ )
+ assert_true(
+ _outcome_is_contradictory(doc),
+ "contradictory outcome not detected: " + rel.string_value(),
+ )
+
+
def main() raises:
TestSuite.discover_tests[__functions_in_module()]().run()