hyf

Context-aware query service for Radroots
git clone https://radroots.dev/git/hyf.git
Log | Files | Refs | README | LICENSE

commit 11fad7d4491313424a6c5f0e8968f33127fe2466
parent 61e3743294648c2e3b8ed97ff4b073924a02757c
Author: triesap <tyson@radroots.org>
Date:   Thu, 24 Sep 2026 19:03:49 +0000

C004: bind v2 scaled money and required output provenance (ADR-0027 D47 BP01)

- Bind snapshot.price as scaled state/amount/scale/currency/basis with closed basis and fees
- Require a closed actual-used provenance object on every successful output
- Update all six v2 schemas, examples, corpus, semantic cases and domain/field mappings
- Keep hyf_v1_jev money schema/domain constructors unchanged and prove the bindings in unit tests

Diffstat:
Mschemas/hyf_ops_v2/buyer_request_interpret.response.schema.json | 42+++++++++++++++++++++++++++++++++++++++++-
Mschemas/hyf_ops_v2/buyer_request_match.request.schema.json | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mschemas/hyf_ops_v2/buyer_request_match.response.schema.json | 33+++++++++++++++++++++++++++++++++
Mschemas/hyf_ops_v2/domain_manifest.json | 9++++++++-
Mschemas/hyf_ops_v2/examples/corpus.json | 32++++++++++++++++++++++++++++++--
Aschemas/hyf_ops_v2/examples/invalid/buyer_request_interpret.response.missing_provenance.json | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aschemas/hyf_ops_v2/examples/invalid/buyer_request_match.response.missing_provenance.json | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aschemas/hyf_ops_v2/examples/invalid/farm_update_interpret.response.missing_provenance.json | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mschemas/hyf_ops_v2/examples/invalid/farm_update_interpret.response.negative_zero_quantity.json | 34++++++++++++++++++++++++++++++----
Aschemas/hyf_ops_v2/examples/invalid/price.adjacent_overflow.json | 7+++++++
Aschemas/hyf_ops_v2/examples/invalid/price.decimal_amount.json | 7+++++++
Aschemas/hyf_ops_v2/examples/invalid/price.fee_decimal_amount.json | 15+++++++++++++++
Aschemas/hyf_ops_v2/examples/invalid/price.fee_missing_kind.json | 14++++++++++++++
Aschemas/hyf_ops_v2/examples/invalid/price.known_with_null_amount.json | 7+++++++
Aschemas/hyf_ops_v2/examples/invalid/price.missing_scale.json | 6++++++
Aschemas/hyf_ops_v2/examples/invalid/price.scale_out_of_range.json | 7+++++++
Aschemas/hyf_ops_v2/examples/invalid/price.unknown_with_value.json | 7+++++++
Aschemas/hyf_ops_v2/examples/invalid/price.unsupported_basis.json | 7+++++++
Aschemas/hyf_ops_v2/examples/invalid/provenance.buyer_with_prior_records.json | 8++++++++
Aschemas/hyf_ops_v2/examples/invalid/provenance.farm_missing_source.json | 4++++
Aschemas/hyf_ops_v2/examples/invalid/provenance.farm_prior_record_without_revision.json | 12++++++++++++
Aschemas/hyf_ops_v2/examples/invalid/provenance.farm_unknown_field.json | 9+++++++++
Aschemas/hyf_ops_v2/examples/invalid/provenance.match_missing_snapshots.json | 6++++++
Aschemas/hyf_ops_v2/examples/invalid/provenance.match_unknown_field.json | 8++++++++
Mschemas/hyf_ops_v2/examples/valid/buyer_request_interpret.response.json | 4++++
Mschemas/hyf_ops_v2/examples/valid/buyer_request_match.request.json | 9++++++++-
Mschemas/hyf_ops_v2/examples/valid/buyer_request_match.response.json | 7+++++++
Aschemas/hyf_ops_v2/examples/valid/buyer_request_match.response.zero_plan_with_provenance.json | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Mschemas/hyf_ops_v2/examples/valid/farm_update_interpret.response.json | 5+++++
Aschemas/hyf_ops_v2/examples/valid/price.known_max_coefficient.json | 7+++++++
Aschemas/hyf_ops_v2/examples/valid/price.known_zero.json | 7+++++++
Aschemas/hyf_ops_v2/examples/valid/price.unknown.json | 7+++++++
Aschemas/hyf_ops_v2/examples/valid/price.with_fees.json | 15+++++++++++++++
Aschemas/hyf_ops_v2/examples/valid/provenance.buyer.json | 10++++++++++
Aschemas/hyf_ops_v2/examples/valid/provenance.farm.json | 8++++++++
Aschemas/hyf_ops_v2/examples/valid/provenance.farm_with_clarification.json | 16++++++++++++++++
Aschemas/hyf_ops_v2/examples/valid/provenance.match.json | 16++++++++++++++++
Aschemas/hyf_ops_v2/examples/valid/provenance.match_empty.json | 7+++++++
Mschemas/hyf_ops_v2/farm_update_interpret.response.schema.json | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mschemas/hyf_ops_v2/manifest.json | 13++++++++++++-
Mschemas/hyf_ops_v2/semantic-cases.json | 13++++++++++++-
Mschemas/hyf_ops_v2/version_manifest.json | 2+-
Mtests/test_hyf.mojo | 113+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
43 files changed, 973 insertions(+), 27 deletions(-)

diff --git a/schemas/hyf_ops_v2/buyer_request_interpret.response.schema.json b/schemas/hyf_ops_v2/buyer_request_interpret.response.schema.json @@ -302,10 +302,44 @@ } ] }, + "provenance_identity": { + "type": "object", + "properties": { + "source_id": {"type": "string", "minLength": 1}, + "revision": {"type": "string", "minLength": 1} + }, + "required": ["source_id", "revision"], + "additionalProperties": false + }, + "clarification_identity": { + "type": "object", + "properties": { + "source_id": {"type": "string", "minLength": 1}, + "revision": {"type": "string", "minLength": 1} + }, + "required": ["source_id", "revision"], + "additionalProperties": false + }, + "interpret_output_provenance": { + "type": "object", + "$comment": "ADR-0027 D47 BP01: immutable identities actually used for every successful buyer interpretation, independently of optional detailed evidence and context.return_provenance. source equals the interpreted input.source revision; clarification is non-null only when that supplied clarification was used. The farm-only prior_records field is forbidden here. Repeated identical identity pairs and identity/coverage disagreement are semantic obligations owned by C023/C030.", + "properties": { + "source": {"$ref": "#/$defs/provenance_identity"}, + "clarification": { + "oneOf": [ + {"type": "null"}, + {"$ref": "#/$defs/clarification_identity"} + ] + } + }, + "required": ["source", "clarification"], + "additionalProperties": false + }, "interpret_output": { "type": "object", "properties": { "versions": {"$ref": "#/$defs/output_versions"}, + "provenance": {"$ref": "#/$defs/interpret_output_provenance"}, "demand_lines": { "type": "array", "items": {"$ref": "#/$defs/demand_line"} @@ -313,7 +347,13 @@ "review": {"$ref": "#/$defs/review"}, "execution": {"$ref": "#/$defs/execution"} }, - "required": ["versions", "demand_lines", "review", "execution"], + "required": [ + "versions", + "provenance", + "demand_lines", + "review", + "execution" + ], "additionalProperties": false } } diff --git a/schemas/hyf_ops_v2/buyer_request_match.request.schema.json b/schemas/hyf_ops_v2/buyer_request_match.request.schema.json @@ -2,7 +2,7 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "hyf_ops_v2/buyer_request_match.request.schema.json", "title": "hyf_ops_v2 buyer_request.match strict request (ADR-0025 D45 CB01-CB04)", - "$comment": "Corrected versioned operation contract (ADR-0026 D46 CR02). Consumes the typed reviewed need, authorized snapshots and coverage; no flattened product_phrase/unreserved_value/scale shortcut. Evidence is the closed span|record_field union.", + "$comment": "Corrected versioned operation contract (ADR-0026 D46 CR02, ADR-0027 D47 BP01). Consumes the typed reviewed need, authorized snapshots and coverage; no flattened product_phrase/unreserved_value/scale shortcut. Evidence is the closed span|record_field union. snapshot.price binds the existing scaled money representation (state/amount/scale/currency/basis) distinct from the reported-quantity decimal projection; the optional fees family is the separate money-schema family.", "type": "object", "properties": { "version": { @@ -357,20 +357,7 @@ "required": ["method", "area", "window"], "additionalProperties": false }, - "price": { - "type": "object", - "properties": { - "state": {"type": "string", "enum": ["known", "unknown"]}, - "amount": { - "type": ["string", "null"], - "pattern": "^(0|[1-9][0-9]*)(\\.[0-9]{1,9})?$" - }, - "currency": {"type": ["string", "null"], "minLength": 3}, - "basis": {"type": ["string", "null"], "minLength": 1} - }, - "required": ["state", "amount", "currency", "basis"], - "additionalProperties": false - } + "price": {"$ref": "#/$defs/snapshot_price"} }, "required": [ "lot_id", @@ -381,6 +368,90 @@ ], "additionalProperties": false }, + "money_fee": { + "type": "object", + "properties": { + "kind": {"type": "string", "minLength": 1}, + "amount": { + "type": "string", + "pattern": "^(0|[1-9][0-9]*)$", + "maxLength": 19 + }, + "scale": {"type": "integer", "minimum": 0, "maximum": 9}, + "currency": {"type": "string", "minLength": 1} + }, + "required": ["kind", "amount", "scale", "currency"], + "additionalProperties": false + }, + "money_fees": { + "type": ["array", "null"], + "items": {"$ref": "#/$defs/money_fee"} + }, + "snapshot_price": { + "type": "object", + "properties": { + "state": {"type": "string", "enum": ["known", "unknown"]}, + "amount": { + "type": ["string", "null"], + "pattern": "^(0|[1-9][0-9]*)$", + "maxLength": 19 + }, + "scale": { + "type": ["integer", "null"], + "minimum": 0, + "maximum": 9 + }, + "currency": {"type": ["string", "null"]}, + "basis": { + "type": "string", + "enum": ["per_unit", "per_pack", "per_order", "unknown"] + }, + "fees": {"$ref": "#/$defs/money_fees"} + }, + "required": ["state", "amount", "scale", "currency", "basis"], + "additionalProperties": false, + "$comment": "ADR-0027 D47 BP01 scaled money. known: amount is a canonical nonnegative integer string (checked coefficient 0..9223372036854775807, enforced by C009), scale 0..9, currency nonblank, basis per_unit|per_pack|per_order; zero is a known price. unknown: amount/scale/currency null and basis unknown; never interpreted as free. No conversion, FX, minimum-order or fee inference.", + "allOf": [ + { + "if": { + "properties": {"state": {"const": "known"}}, + "required": ["state"] + }, + "then": { + "required": ["amount", "scale", "currency", "basis"], + "properties": { + "amount": { + "type": "string", + "pattern": "^(0|[1-9][0-9]*)$", + "maxLength": 19 + }, + "scale": { + "type": "integer", + "minimum": 0, + "maximum": 9 + }, + "currency": {"type": "string", "minLength": 1}, + "basis": {"enum": ["per_unit", "per_pack", "per_order"]} + } + } + }, + { + "if": { + "properties": {"state": {"const": "unknown"}}, + "required": ["state"] + }, + "then": { + "required": ["amount", "scale", "currency", "basis"], + "properties": { + "amount": {"type": "null"}, + "scale": {"type": "null"}, + "currency": {"type": "null"}, + "basis": {"const": "unknown"} + } + } + } + ] + }, "coverage": { "type": "object", "properties": { diff --git a/schemas/hyf_ops_v2/buyer_request_match.response.schema.json b/schemas/hyf_ops_v2/buyer_request_match.response.schema.json @@ -325,10 +325,42 @@ } ] }, + "need_identity": { + "type": "object", + "properties": { + "need_id": {"type": "string", "minLength": 1}, + "revision": {"type": "string", "minLength": 1} + }, + "required": ["need_id", "revision"], + "additionalProperties": false + }, + "snapshot_identity": { + "type": "object", + "properties": { + "lot_id": {"type": "string", "minLength": 1}, + "revision": {"type": "string", "minLength": 1} + }, + "required": ["lot_id", "revision"], + "additionalProperties": false + }, + "match_output_provenance": { + "type": "object", + "$comment": "ADR-0027 D47 BP01: immutable identities actually used for every successful match, independently of optional detailed evidence and context.return_provenance. need equals the interpreted input.need identity/revision; snapshots lists the supplied snapshots actually assessed, even when assessments fail or no plans result. An empty list means no snapshots were assessed; a nonempty assessed scope with zero plans still records its revisions. Never copy all supplied inputs as proof of use. Repeated identical pairs and identity/coverage-allocation disagreement are semantic obligations owned by C023/C041.", + "properties": { + "need": {"$ref": "#/$defs/need_identity"}, + "snapshots": { + "type": "array", + "items": {"$ref": "#/$defs/snapshot_identity"} + } + }, + "required": ["need", "snapshots"], + "additionalProperties": false + }, "match_output": { "type": "object", "properties": { "versions": {"$ref": "#/$defs/output_versions"}, + "provenance": {"$ref": "#/$defs/match_output_provenance"}, "assessments": { "type": "array", "items": {"$ref": "#/$defs/assessment"} @@ -342,6 +374,7 @@ }, "required": [ "versions", + "provenance", "assessments", "plans", "limitations", diff --git a/schemas/hyf_ops_v2/domain_manifest.json b/schemas/hyf_ops_v2/domain_manifest.json @@ -26,9 +26,16 @@ {"schema_path": "output.claims/proposed_changes", "domain": "hyf_application.farm_review_output", "owner": "C008/C009/C030", "status": "schema_bound"}, {"schema_path": "output.demand_lines", "domain": "hyf_application.buyer_needs", "owner": "C008/C009/C030", "status": "schema_bound"}, {"schema_path": "output.assessments/plans/limitations", "domain": "hyf_application.match_checks / match_plan / match_index", "owner": "C008/C009/C038", "status": "schema_bound"}, - {"schema_path": "output.execution", "domain": "hyf_core.domain.execution.execution_state", "owner": "C008/C009", "status": "schema_bound"} + {"schema_path": "output.execution", "domain": "hyf_core.domain.execution.execution_state", "owner": "C008/C009", "status": "schema_bound"}, + {"schema_path": "input.snapshots[].price", "domain": "hyf_core.domain.price PriceCondition/known_price scaled amount/scale/currency/basis (D23 scaled money, distinct from the reported_quantity decimal projection)", "owner": "C009/C023", "status": "schema_bound; the wire state/amount/scale/currency/basis maps to the existing scaled money representation; no conversion, FX, minimum-order or fee inference"}, + {"schema_path": "input.snapshots[].price.fees", "domain": "schemas/hyf_v1_jev/money.schema.json properties.fees closed {kind,amount,scale,currency} family", "owner": "C009", "status": "schema_bound; optional closed family, legacy money schema retained unchanged as provenance"}, + {"schema_path": "output.provenance (farm_update.interpret)", "domain": "immutable source/clarification/prior-record identities actually used", "owner": "C009/C023/C030", "status": "schema_bound; required on every success independently of context.return_provenance"}, + {"schema_path": "output.provenance (buyer_request.interpret)", "domain": "immutable source/clarification identities actually used", "owner": "C009/C023/C030", "status": "schema_bound; required on every success; farm-only prior_records forbidden"}, + {"schema_path": "output.provenance (buyer_request.match)", "domain": "immutable need and evaluated snapshot identities actually used", "owner": "C009/C023/C041", "status": "schema_bound; required on every success, including no-match/zero-plan results; coverage/allocation identities must agree"} ], "authority_integration": "docs/spec/hyf_v1_jev/specs/AUTHORITY_INTEGRATION.md: HYF is read-only; supplied records are host-authorized preconditions, HYF validates consistency but performs no writes and claims no authentication.", "clarification_reconciliation": "ADR-0026 D46 CR02 separates the clarification source identity/revision (input.clarification.revision) from the primary target revision (input.clarification.expected_revision, which equals input.source.revision). The legacy ClarificationEvidence constructor is preserved unchanged until its planned C009/C023 migration; the schema binding and this mapping are the C004 obligation.", + "money_reconciliation": "ADR-0027 D47 BP01: the v2 snapshot.price copy binds the existing scaled money representation as canonical nonnegative integer amount/scale 0..9/nonblank currency/closed basis. Wire state=unknown with null amount/scale/currency maps mechanically to the existing explicit unknown Price alternative (whose internal placeholders are 0/0/empty); state governs unknown and no numeric zero or free price is inferred. schemas/hyf_v1_jev/money.schema.json and the domain constructors are unchanged and are not reused as the strict v2 validator.", + "provenance_reconciliation": "ADR-0027 D47 BP01: output.provenance is required on every successful v2 result and carries only the immutable identities actually used. It is independent of optional detailed evidence and of context.return_provenance; the flag continues to govern optional detailed evidence only. Identity equality, actual use, uniqueness and coverage/allocation agreement are semantic obligations owned by C009/C023/C030/C038/C041; C004 binds the closed wire shapes and executes the paired controls.", "unavailability": "C004 parses the corrected context and then refuses dispatch with capability_unavailable until C042-C046 activate the operations." } diff --git a/schemas/hyf_ops_v2/examples/corpus.json b/schemas/hyf_ops_v2/examples/corpus.json @@ -2,7 +2,7 @@ "schema_version": 1, "program": "hyf_http_v1", "revision": "hyf_ops_v2", - "note": "Reference-validation corpus for C004 (ADR-0026 D46 CR01-CR03 successor). schema_valid records the JSON Schema outcome; semantic names a labelled obligation that JSON Schema cannot express and that is exercised separately (semantic-cases.json). Targets: '<operation>.<request|response>' uses that operation schema; 'def:<name>' validates against that named $defs entry in any hyf_ops_v2 schema (resolved across all schemas, first match). The CR01 envelope algebra and CR02 linkage/degradation controls are executed programmatically by the successor reference diagnostic (docs/execution/evidence/hyf_http_v1/C004-requalification/reference-schema-validate.v2.py.txt).", + "note": "Reference-validation corpus for C004 (ADR-0026 D46 CR01-CR03 and ADR-0027 D47 BP01-BP03 successor). schema_valid records the JSON Schema outcome; semantic names a labelled obligation that JSON Schema cannot express and that is exercised separately (semantic-cases.json). Targets: '<operation>.<request|response>' uses that operation schema; 'def:<name>' validates against that named $defs entry in any hyf_ops_v2 schema (resolved across all schemas, first match). The CR01 envelope algebra, BP01 money/provenance and CR02 linkage/degradation controls are executed programmatically by the successor reference diagnostic (docs/execution/evidence/hyf_http_v1/C004-requalification-2/reference-schema-validate.v3.py.txt).", "entries": [ {"file": "examples/valid/farm_update_interpret.request.json", "target": "farm_update.interpret.request", "schema_valid": true}, {"file": "examples/valid/farm_update_interpret.response.json", "target": "farm_update.interpret.response", "schema_valid": true}, @@ -43,6 +43,34 @@ {"file": "examples/invalid/buyer_request_interpret.request.farm_id_forbidden.json", "target": "buyer_request.interpret.request", "schema_valid": false}, {"file": "examples/invalid/buyer_request_match.request.missing_references.json", "target": "buyer_request.match.request", "schema_valid": false}, {"file": "examples/invalid/buyer_request_match.request.need_actor_missing.json", "target": "buyer_request.match.request", "schema_valid": false}, - {"file": "examples/invalid/buyer_request_match.request.unknown_selector.json", "target": "buyer_request.match.request", "schema_valid": false, "semantic": "unknown_selector"} + {"file": "examples/invalid/buyer_request_match.request.unknown_selector.json", "target": "buyer_request.match.request", "schema_valid": false, "semantic": "unknown_selector"}, + {"file": "examples/valid/price.known_zero.json", "target": "def:snapshot_price", "schema_valid": true}, + {"file": "examples/valid/price.known_max_coefficient.json", "target": "def:snapshot_price", "schema_valid": true}, + {"file": "examples/valid/price.unknown.json", "target": "def:snapshot_price", "schema_valid": true}, + {"file": "examples/valid/price.with_fees.json", "target": "def:snapshot_price", "schema_valid": true}, + {"file": "examples/invalid/price.known_with_null_amount.json", "target": "def:snapshot_price", "schema_valid": false, "semantic": "price_known_requires_scaled_facts"}, + {"file": "examples/invalid/price.missing_scale.json", "target": "def:snapshot_price", "schema_valid": false, "semantic": "price_known_requires_scaled_facts"}, + {"file": "examples/invalid/price.unknown_with_value.json", "target": "def:snapshot_price", "schema_valid": false, "semantic": "price_unknown_is_not_zero"}, + {"file": "examples/invalid/price.decimal_amount.json", "target": "def:snapshot_price", "schema_valid": false, "semantic": "price_known_requires_scaled_facts"}, + {"file": "examples/invalid/price.unsupported_basis.json", "target": "def:snapshot_price", "schema_valid": false, "semantic": "price_basis_closed"}, + {"file": "examples/invalid/price.scale_out_of_range.json", "target": "def:snapshot_price", "schema_valid": false, "semantic": "price_known_requires_scaled_facts"}, + {"file": "examples/invalid/price.adjacent_overflow.json", "target": "def:snapshot_price", "schema_valid": true, "semantic": "price_coefficient_int64_bound"}, + {"file": "examples/invalid/price.fee_missing_kind.json", "target": "def:snapshot_price", "schema_valid": false, "semantic": "price_fee_family_closed"}, + {"file": "examples/invalid/price.fee_decimal_amount.json", "target": "def:snapshot_price", "schema_valid": false, "semantic": "price_fee_family_closed"}, + {"file": "examples/valid/provenance.farm.json", "target": "def:farm_output_provenance", "schema_valid": true}, + {"file": "examples/valid/provenance.farm_with_clarification.json", "target": "def:farm_output_provenance", "schema_valid": true}, + {"file": "examples/valid/provenance.buyer.json", "target": "def:interpret_output_provenance", "schema_valid": true}, + {"file": "examples/valid/provenance.match_empty.json", "target": "def:match_output_provenance", "schema_valid": true}, + {"file": "examples/valid/provenance.match.json", "target": "def:match_output_provenance", "schema_valid": true}, + {"file": "examples/invalid/provenance.farm_missing_source.json", "target": "def:farm_output_provenance", "schema_valid": false, "semantic": "provenance_required_on_success"}, + {"file": "examples/invalid/provenance.farm_prior_record_without_revision.json", "target": "def:farm_output_provenance", "schema_valid": false, "semantic": "provenance_identity_closed"}, + {"file": "examples/invalid/provenance.farm_unknown_field.json", "target": "def:farm_output_provenance", "schema_valid": false, "semantic": "provenance_identity_closed"}, + {"file": "examples/invalid/provenance.buyer_with_prior_records.json", "target": "def:interpret_output_provenance", "schema_valid": false, "semantic": "provenance_farm_prior_records_only"}, + {"file": "examples/invalid/provenance.match_missing_snapshots.json", "target": "def:match_output_provenance", "schema_valid": false, "semantic": "provenance_required_on_success"}, + {"file": "examples/invalid/provenance.match_unknown_field.json", "target": "def:match_output_provenance", "schema_valid": false, "semantic": "provenance_identity_closed"}, + {"file": "examples/invalid/farm_update_interpret.response.missing_provenance.json", "target": "farm_update.interpret.response", "schema_valid": false, "semantic": "provenance_required_on_success"}, + {"file": "examples/invalid/buyer_request_interpret.response.missing_provenance.json", "target": "buyer_request.interpret.response", "schema_valid": false, "semantic": "provenance_required_on_success"}, + {"file": "examples/invalid/buyer_request_match.response.missing_provenance.json", "target": "buyer_request.match.response", "schema_valid": false, "semantic": "provenance_required_on_success"}, + {"file": "examples/valid/buyer_request_match.response.zero_plan_with_provenance.json", "target": "buyer_request.match.response", "schema_valid": true} ] } diff --git a/schemas/hyf_ops_v2/examples/invalid/buyer_request_interpret.response.missing_provenance.json b/schemas/hyf_ops_v2/examples/invalid/buyer_request_interpret.response.missing_provenance.json @@ -0,0 +1,86 @@ +{ + "version": 1, + "request_id": "buyer-interpret-req-1", + "ok": true, + "output": { + "versions": { + "schema": "hyf_ops_v2", + "taxonomy": "hyf_ops_v2.taxonomy.v1", + "normalization": "hyf_ops_v2.normalization.v1", + "review_policy": "hyf_ops_v2.review_policy.v1", + "ranking_policy": "hyf_ops_v2.ranking_policy.v1", + "question_bundle": "hyf_ops_v2.question_bundle.v1", + "model": "jev-1.13.0" + }, + "demand_lines": [ + { + "line_id": "line-1", + "product": { + "phrase": "Roma tomatoes", + "catalogue_id": "tomato.roma", + "resolution": "resolved" + }, + "quantity": { + "state": "known", + "value": "25", + "unit": "lb", + "qualifier": "exact" + }, + "conditions": [ + { + "kind": "fulfillment", + "strength": "mandatory", + "value": "pickup", + "evidence": [ + { + "kind": "span", + "source_id": "src-buyer-1", + "revision": "rev-3", + "start": 55, + "end": 61, + "method": "span" + } + ] + }, + { + "kind": "window", + "strength": "preferred", + "value": "2026-09-26/2026-09-27", + "evidence": [ + { + "kind": "span", + "source_id": "src-buyer-1", + "revision": "rev-3", + "start": 62, + "end": 71, + "method": "span" + } + ] + } + ], + "evidence": [ + { + "kind": "span", + "source_id": "src-buyer-1", + "revision": "rev-3", + "start": 12, + "end": 39, + "method": "span" + } + ], + "review_required": false + } + ], + "review": { + "required": false, + "clarifications": [] + }, + "execution": { + "status": "complete", + "provider_calls": 0, + "model": null, + "question_bundle": null, + "degraded_reason": null + } + } +} diff --git a/schemas/hyf_ops_v2/examples/invalid/buyer_request_match.response.missing_provenance.json b/schemas/hyf_ops_v2/examples/invalid/buyer_request_match.response.missing_provenance.json @@ -0,0 +1,102 @@ +{ + "version": 1, + "request_id": "buyer-match-req-1", + "ok": true, + "output": { + "versions": { + "schema": "hyf_ops_v2", + "taxonomy": "hyf_ops_v2.taxonomy.v1", + "normalization": "hyf_ops_v2.normalization.v1", + "review_policy": "hyf_ops_v2.review_policy.v1", + "ranking_policy": "hyf_ops_v2.ranking_policy.v1", + "question_bundle": "hyf_ops_v2.question_bundle.v1", + "model": "jev-1.13.0" + }, + "assessments": [ + { + "candidate_id": "lot-1", + "eligibility": "eligible", + "checks": [ + { + "kind": "product", + "result": "pass", + "reason": "product_match", + "evidence": [] + }, + { + "kind": "availability", + "result": "pass", + "reason": "availability_known", + "evidence": [] + }, + { + "kind": "quantity", + "result": "pass", + "reason": "quantity_sufficient", + "evidence": [] + } + ] + }, + { + "candidate_id": "lot-2", + "eligibility": "conditional", + "checks": [ + { + "kind": "product", + "result": "unknown", + "reason": "product_unresolved", + "evidence": [] + }, + { + "kind": "availability", + "result": "unknown", + "reason": "stock_unknown", + "evidence": [] + } + ] + } + ], + "plans": [ + { + "plan_id": "plan-1", + "supplier_id": "farm-1", + "allocations": [ + { + "lot_id": "lot-1", + "revision": "rev-9", + "line_id": "line-1", + "quantity": { + "state": "known", + "value": "25", + "unit": "lb", + "qualifier": "exact" + } + } + ], + "coverage": { + "lines_covered": [ + "line-1" + ], + "uncovered": [], + "partial": false + }, + "unresolved": [] + } + ], + "limitations": { + "scope": "supplied_only", + "truncated": false, + "supported_mode": "single_supplier_compatible_lots", + "unsupported": [ + "multi_supplier" + ] + }, + "execution": { + "status": "complete", + "provider_calls": 0, + "model": null, + "question_bundle": null, + "degraded_reason": null + } + } +} diff --git a/schemas/hyf_ops_v2/examples/invalid/farm_update_interpret.response.missing_provenance.json b/schemas/hyf_ops_v2/examples/invalid/farm_update_interpret.response.missing_provenance.json @@ -0,0 +1,81 @@ +{ + "version": 1, + "request_id": "farm-interpret-req-1", + "trace_id": "farm-interpret-trace-1", + "ok": true, + "output": { + "versions": { + "schema": "hyf_ops_v2", + "taxonomy": "hyf_ops_v2.taxonomy.v1", + "normalization": "hyf_ops_v2.normalization.v1", + "review_policy": "hyf_ops_v2.review_policy.v1", + "ranking_policy": "hyf_ops_v2.ranking_policy.v1", + "question_bundle": "hyf_ops_v2.question_bundle.v1", + "model": "jev-1.13.0" + }, + "claims": [ + { + "claim_id": "claim-1", + "product": { + "phrase": "Roma tomatoes", + "catalogue_id": "tomato.roma", + "resolution": "resolved" + }, + "status": "offered", + "quantity": { + "state": "known", + "value": "80.00", + "unit": "lb", + "qualifier": "approximate" + }, + "evidence": [ + { + "kind": "span", + "source_id": "src-farm-1", + "revision": "rev-7", + "start": 11, + "end": 21, + "method": "span" + } + ], + "review_required": true + } + ], + "proposed_changes": [ + { + "target": { + "kind": "listing", + "id": "listing-1" + }, + "operation": "remaining", + "expected_revision": "rev-2", + "evidence": [ + { + "kind": "span", + "source_id": "src-farm-1", + "revision": "rev-7", + "start": 11, + "end": 21, + "method": "span" + } + ] + } + ], + "review": { + "required": true, + "clarifications": [ + { + "field": "quantity", + "reason": "approximation_requested" + } + ] + }, + "execution": { + "status": "complete", + "provider_calls": 0, + "model": null, + "question_bundle": null, + "degraded_reason": null + } + } +} diff --git a/schemas/hyf_ops_v2/examples/invalid/farm_update_interpret.response.negative_zero_quantity.json b/schemas/hyf_ops_v2/examples/invalid/farm_update_interpret.response.negative_zero_quantity.json @@ -15,15 +15,41 @@ "claims": [ { "claim_id": "claim-1", - "product": {"phrase": "Roma tomatoes", "catalogue_id": "tomato.roma", "resolution": "resolved"}, + "product": { + "phrase": "Roma tomatoes", + "catalogue_id": "tomato.roma", + "resolution": "resolved" + }, "status": "offered", - "quantity": {"state": "known", "value": "-0", "unit": "lb", "qualifier": "exact"}, + "quantity": { + "state": "known", + "value": "-0", + "unit": "lb", + "qualifier": "exact" + }, "evidence": [], "review_required": false } ], "proposed_changes": [], - "review": {"required": false, "clarifications": []}, - "execution": {"status": "complete", "provider_calls": 0, "model": null, "question_bundle": null, "degraded_reason": null} + "review": { + "required": false, + "clarifications": [] + }, + "execution": { + "status": "complete", + "provider_calls": 0, + "model": null, + "question_bundle": null, + "degraded_reason": null + }, + "provenance": { + "source": { + "source_id": "src-farm-1", + "revision": "rev-7" + }, + "clarification": null, + "prior_records": [] + } } } diff --git a/schemas/hyf_ops_v2/examples/invalid/price.adjacent_overflow.json b/schemas/hyf_ops_v2/examples/invalid/price.adjacent_overflow.json @@ -0,0 +1,7 @@ +{ + "state": "known", + "amount": "9223372036854775808", + "scale": 0, + "currency": "CAD", + "basis": "per_unit" +} diff --git a/schemas/hyf_ops_v2/examples/invalid/price.decimal_amount.json b/schemas/hyf_ops_v2/examples/invalid/price.decimal_amount.json @@ -0,0 +1,7 @@ +{ + "state": "known", + "amount": "2.50", + "scale": 2, + "currency": "CAD", + "basis": "per_unit" +} diff --git a/schemas/hyf_ops_v2/examples/invalid/price.fee_decimal_amount.json b/schemas/hyf_ops_v2/examples/invalid/price.fee_decimal_amount.json @@ -0,0 +1,15 @@ +{ + "state": "known", + "amount": "250", + "scale": 2, + "currency": "CAD", + "basis": "per_unit", + "fees": [ + { + "kind": "delivery", + "amount": "5.00", + "scale": 2, + "currency": "CAD" + } + ] +} diff --git a/schemas/hyf_ops_v2/examples/invalid/price.fee_missing_kind.json b/schemas/hyf_ops_v2/examples/invalid/price.fee_missing_kind.json @@ -0,0 +1,14 @@ +{ + "state": "known", + "amount": "250", + "scale": 2, + "currency": "CAD", + "basis": "per_unit", + "fees": [ + { + "amount": "500", + "scale": 2, + "currency": "CAD" + } + ] +} diff --git a/schemas/hyf_ops_v2/examples/invalid/price.known_with_null_amount.json b/schemas/hyf_ops_v2/examples/invalid/price.known_with_null_amount.json @@ -0,0 +1,7 @@ +{ + "state": "known", + "amount": null, + "scale": 2, + "currency": "CAD", + "basis": "per_unit" +} diff --git a/schemas/hyf_ops_v2/examples/invalid/price.missing_scale.json b/schemas/hyf_ops_v2/examples/invalid/price.missing_scale.json @@ -0,0 +1,6 @@ +{ + "state": "known", + "amount": "250", + "currency": "CAD", + "basis": "per_unit" +} diff --git a/schemas/hyf_ops_v2/examples/invalid/price.scale_out_of_range.json b/schemas/hyf_ops_v2/examples/invalid/price.scale_out_of_range.json @@ -0,0 +1,7 @@ +{ + "state": "known", + "amount": "250", + "scale": 10, + "currency": "CAD", + "basis": "per_unit" +} diff --git a/schemas/hyf_ops_v2/examples/invalid/price.unknown_with_value.json b/schemas/hyf_ops_v2/examples/invalid/price.unknown_with_value.json @@ -0,0 +1,7 @@ +{ + "state": "unknown", + "amount": "250", + "scale": 2, + "currency": "CAD", + "basis": "unknown" +} diff --git a/schemas/hyf_ops_v2/examples/invalid/price.unsupported_basis.json b/schemas/hyf_ops_v2/examples/invalid/price.unsupported_basis.json @@ -0,0 +1,7 @@ +{ + "state": "known", + "amount": "250", + "scale": 2, + "currency": "CAD", + "basis": "per_lb" +} diff --git a/schemas/hyf_ops_v2/examples/invalid/provenance.buyer_with_prior_records.json b/schemas/hyf_ops_v2/examples/invalid/provenance.buyer_with_prior_records.json @@ -0,0 +1,8 @@ +{ + "source": { + "source_id": "src-buyer-1", + "revision": "rev-3" + }, + "clarification": null, + "prior_records": [] +} diff --git a/schemas/hyf_ops_v2/examples/invalid/provenance.farm_missing_source.json b/schemas/hyf_ops_v2/examples/invalid/provenance.farm_missing_source.json @@ -0,0 +1,4 @@ +{ + "clarification": null, + "prior_records": [] +} diff --git a/schemas/hyf_ops_v2/examples/invalid/provenance.farm_prior_record_without_revision.json b/schemas/hyf_ops_v2/examples/invalid/provenance.farm_prior_record_without_revision.json @@ -0,0 +1,12 @@ +{ + "source": { + "source_id": "src-farm-1", + "revision": "rev-7" + }, + "clarification": null, + "prior_records": [ + { + "record_id": "record-100" + } + ] +} diff --git a/schemas/hyf_ops_v2/examples/invalid/provenance.farm_unknown_field.json b/schemas/hyf_ops_v2/examples/invalid/provenance.farm_unknown_field.json @@ -0,0 +1,9 @@ +{ + "source": { + "source_id": "src-farm-1", + "revision": "rev-7" + }, + "clarification": null, + "prior_records": [], + "snapshots": [] +} diff --git a/schemas/hyf_ops_v2/examples/invalid/provenance.match_missing_snapshots.json b/schemas/hyf_ops_v2/examples/invalid/provenance.match_missing_snapshots.json @@ -0,0 +1,6 @@ +{ + "need": { + "need_id": "need-1", + "revision": "rev-4" + } +} diff --git a/schemas/hyf_ops_v2/examples/invalid/provenance.match_unknown_field.json b/schemas/hyf_ops_v2/examples/invalid/provenance.match_unknown_field.json @@ -0,0 +1,8 @@ +{ + "need": { + "need_id": "need-1", + "revision": "rev-4" + }, + "snapshots": [], + "assessments": [] +} diff --git a/schemas/hyf_ops_v2/examples/valid/buyer_request_interpret.response.json b/schemas/hyf_ops_v2/examples/valid/buyer_request_interpret.response.json @@ -12,6 +12,10 @@ "question_bundle": "hyf_ops_v2.question_bundle.v1", "model": "jev-1.13.0" }, + "provenance": { + "source": {"source_id": "src-buyer-1", "revision": "rev-3"}, + "clarification": {"source_id": "src-clar-2", "revision": "rev-1"} + }, "demand_lines": [ { "line_id": "line-1", diff --git a/schemas/hyf_ops_v2/examples/valid/buyer_request_match.request.json b/schemas/hyf_ops_v2/examples/valid/buyer_request_match.request.json @@ -58,7 +58,14 @@ "unreserved_quantity": {"state": "known", "value": "120", "unit": "lb", "qualifier": "exact"}, "attributes": [{"name": "grade-A", "verified": true}], "fulfillment": {"method": "pickup", "area": "Vancouver", "window": "2026-09-26/2026-09-27"}, - "price": {"state": "known", "amount": "2.50", "currency": "CAD", "basis": "per_lb"} + "price": { + "state": "known", + "amount": "250", + "scale": 2, + "currency": "CAD", + "basis": "per_unit", + "fees": [{"kind": "delivery", "amount": "500", "scale": 2, "currency": "CAD"}] + } }, { "lot_id": "lot-2", diff --git a/schemas/hyf_ops_v2/examples/valid/buyer_request_match.response.json b/schemas/hyf_ops_v2/examples/valid/buyer_request_match.response.json @@ -12,6 +12,13 @@ "question_bundle": "hyf_ops_v2.question_bundle.v1", "model": "jev-1.13.0" }, + "provenance": { + "need": {"need_id": "need-1", "revision": "rev-4"}, + "snapshots": [ + {"lot_id": "lot-1", "revision": "rev-9"}, + {"lot_id": "lot-2", "revision": "rev-4"} + ] + }, "assessments": [ { "candidate_id": "lot-1", diff --git a/schemas/hyf_ops_v2/examples/valid/buyer_request_match.response.zero_plan_with_provenance.json b/schemas/hyf_ops_v2/examples/valid/buyer_request_match.response.zero_plan_with_provenance.json @@ -0,0 +1,49 @@ +{ + "version": 1, + "request_id": "buyer-match-req-1", + "ok": true, + "output": { + "versions": { + "schema": "hyf_ops_v2", + "taxonomy": "hyf_ops_v2.taxonomy.v1", + "normalization": "hyf_ops_v2.normalization.v1", + "review_policy": "hyf_ops_v2.review_policy.v1", + "ranking_policy": "hyf_ops_v2.ranking_policy.v1", + "question_bundle": "hyf_ops_v2.question_bundle.v1", + "model": "jev-1.13.0" + }, + "provenance": { + "need": { + "need_id": "need-1", + "revision": "rev-4" + }, + "snapshots": [ + { + "lot_id": "lot-1", + "revision": "rev-9" + }, + { + "lot_id": "lot-2", + "revision": "rev-4" + } + ] + }, + "assessments": [], + "plans": [], + "limitations": { + "scope": "supplied_only", + "truncated": false, + "supported_mode": "single_supplier_compatible_lots", + "unsupported": [ + "multi_supplier" + ] + }, + "execution": { + "status": "complete", + "provider_calls": 0, + "model": null, + "question_bundle": null, + "degraded_reason": null + } + } +} diff --git a/schemas/hyf_ops_v2/examples/valid/farm_update_interpret.response.json b/schemas/hyf_ops_v2/examples/valid/farm_update_interpret.response.json @@ -13,6 +13,11 @@ "question_bundle": "hyf_ops_v2.question_bundle.v1", "model": "jev-1.13.0" }, + "provenance": { + "source": {"source_id": "src-farm-1", "revision": "rev-7"}, + "clarification": {"source_id": "src-clar-1", "revision": "rev-1"}, + "prior_records": [{"record_id": "record-100", "revision": "rev-2"}] + }, "claims": [ { "claim_id": "claim-1", diff --git a/schemas/hyf_ops_v2/examples/valid/price.known_max_coefficient.json b/schemas/hyf_ops_v2/examples/valid/price.known_max_coefficient.json @@ -0,0 +1,7 @@ +{ + "state": "known", + "amount": "9223372036854775807", + "scale": 9, + "currency": "CAD", + "basis": "per_order" +} diff --git a/schemas/hyf_ops_v2/examples/valid/price.known_zero.json b/schemas/hyf_ops_v2/examples/valid/price.known_zero.json @@ -0,0 +1,7 @@ +{ + "state": "known", + "amount": "0", + "scale": 0, + "currency": "CAD", + "basis": "per_unit" +} diff --git a/schemas/hyf_ops_v2/examples/valid/price.unknown.json b/schemas/hyf_ops_v2/examples/valid/price.unknown.json @@ -0,0 +1,7 @@ +{ + "state": "unknown", + "amount": null, + "scale": null, + "currency": null, + "basis": "unknown" +} diff --git a/schemas/hyf_ops_v2/examples/valid/price.with_fees.json b/schemas/hyf_ops_v2/examples/valid/price.with_fees.json @@ -0,0 +1,15 @@ +{ + "state": "known", + "amount": "250", + "scale": 2, + "currency": "CAD", + "basis": "per_unit", + "fees": [ + { + "kind": "delivery", + "amount": "500", + "scale": 2, + "currency": "CAD" + } + ] +} diff --git a/schemas/hyf_ops_v2/examples/valid/provenance.buyer.json b/schemas/hyf_ops_v2/examples/valid/provenance.buyer.json @@ -0,0 +1,10 @@ +{ + "source": { + "source_id": "src-buyer-1", + "revision": "rev-3" + }, + "clarification": { + "source_id": "src-clar-2", + "revision": "rev-1" + } +} diff --git a/schemas/hyf_ops_v2/examples/valid/provenance.farm.json b/schemas/hyf_ops_v2/examples/valid/provenance.farm.json @@ -0,0 +1,8 @@ +{ + "source": { + "source_id": "src-farm-1", + "revision": "rev-7" + }, + "clarification": null, + "prior_records": [] +} diff --git a/schemas/hyf_ops_v2/examples/valid/provenance.farm_with_clarification.json b/schemas/hyf_ops_v2/examples/valid/provenance.farm_with_clarification.json @@ -0,0 +1,16 @@ +{ + "source": { + "source_id": "src-farm-1", + "revision": "rev-7" + }, + "clarification": { + "source_id": "src-clar-1", + "revision": "rev-1" + }, + "prior_records": [ + { + "record_id": "record-100", + "revision": "rev-2" + } + ] +} diff --git a/schemas/hyf_ops_v2/examples/valid/provenance.match.json b/schemas/hyf_ops_v2/examples/valid/provenance.match.json @@ -0,0 +1,16 @@ +{ + "need": { + "need_id": "need-1", + "revision": "rev-4" + }, + "snapshots": [ + { + "lot_id": "lot-1", + "revision": "rev-9" + }, + { + "lot_id": "lot-2", + "revision": "rev-4" + } + ] +} diff --git a/schemas/hyf_ops_v2/examples/valid/provenance.match_empty.json b/schemas/hyf_ops_v2/examples/valid/provenance.match_empty.json @@ -0,0 +1,7 @@ +{ + "need": { + "need_id": "need-1", + "revision": "rev-4" + }, + "snapshots": [] +} diff --git a/schemas/hyf_ops_v2/farm_update_interpret.response.schema.json b/schemas/hyf_ops_v2/farm_update_interpret.response.schema.json @@ -334,10 +334,57 @@ } ] }, + "provenance_identity": { + "type": "object", + "properties": { + "source_id": {"type": "string", "minLength": 1}, + "revision": {"type": "string", "minLength": 1} + }, + "required": ["source_id", "revision"], + "additionalProperties": false + }, + "clarification_identity": { + "type": "object", + "properties": { + "source_id": {"type": "string", "minLength": 1}, + "revision": {"type": "string", "minLength": 1} + }, + "required": ["source_id", "revision"], + "additionalProperties": false + }, + "prior_record_identity": { + "type": "object", + "properties": { + "record_id": {"type": "string", "minLength": 1}, + "revision": {"type": "string", "minLength": 1} + }, + "required": ["record_id", "revision"], + "additionalProperties": false + }, + "farm_output_provenance": { + "type": "object", + "$comment": "ADR-0027 D47 BP01: immutable identities actually used for every successful farm interpretation, independently of optional detailed evidence and context.return_provenance. source equals the interpreted input.source revision; clarification is non-null only when that supplied clarification was used; prior_records lists only supplied records actually consulted. Repeated identical identity/revision pairs and identity/coverage disagreement are semantic obligations owned by C023/C030.", + "properties": { + "source": {"$ref": "#/$defs/provenance_identity"}, + "clarification": { + "oneOf": [ + {"type": "null"}, + {"$ref": "#/$defs/clarification_identity"} + ] + }, + "prior_records": { + "type": "array", + "items": {"$ref": "#/$defs/prior_record_identity"} + } + }, + "required": ["source", "clarification", "prior_records"], + "additionalProperties": false + }, "farm_output": { "type": "object", "properties": { "versions": {"$ref": "#/$defs/output_versions"}, + "provenance": {"$ref": "#/$defs/farm_output_provenance"}, "claims": { "type": "array", "items": {"$ref": "#/$defs/claim"} @@ -351,6 +398,7 @@ }, "required": [ "versions", + "provenance", "claims", "proposed_changes", "review", diff --git a/schemas/hyf_ops_v2/manifest.json b/schemas/hyf_ops_v2/manifest.json @@ -36,5 +36,16 @@ }, "semantic_cases": "semantic-cases.json", "version_manifest": "version_manifest.json", - "domain_manifest": "domain_manifest.json" + "domain_manifest": "domain_manifest.json", + "money_binding": { + "schema": "buyer_request_match.request.schema.json#/$defs/snapshot_price", + "fees": "buyer_request_match.request.schema.json#/$defs/money_fees", + "note": "ADR-0027 D47 BP01: snapshot.price binds the existing scaled money representation (state/amount/scale/currency/basis) distinct from the reported-quantity decimal projection. known requires a canonical nonnegative integer amount, integer scale 0..9, nonblank currency and basis per_unit|per_pack|per_order (zero is known); unknown requires amount/scale/currency null and basis unknown and is never free. Optional closed fees {kind,amount,scale,currency} are the separate money-schema family. Legacy hyf_v1_jev money schema/domain constructors are unchanged; C009 enforces the checked int64 coefficient and typed mapping." + }, + "provenance_binding": { + "farm": "farm_update_interpret.response.schema.json#/$defs/farm_output_provenance", + "buyer": "buyer_request_interpret.response.schema.json#/$defs/interpret_output_provenance", + "match": "buyer_request_match.response.schema.json#/$defs/match_output_provenance", + "note": "ADR-0027 D47 BP01: every successful output requires a closed provenance object carrying only the immutable identities actually used, independently of optional detailed evidence and context.return_provenance. Error envelopes carry no output/provenance. Identity equality, actual use, uniqueness and coverage/allocation agreement are semantic obligations owned by C009/C023/C030/C038/C041." + } } diff --git a/schemas/hyf_ops_v2/semantic-cases.json b/schemas/hyf_ops_v2/semantic-cases.json @@ -32,6 +32,17 @@ {"id": "coverage_count_disagreement", "path": "input.coverage evaluated/excluded counts disagree with actual inputs/results", "expect": "reject", "wire": "invalid_request", "owner": "C023", "enforced_by": "supplied_only coverage accounting; C023 typed consistency"}, {"id": "deterministic_path_reports_model_non_use", "path": "context.versions.model present with deterministic execution", "expect": "accepted_no_call", "wire": "capability_unavailable (pre-activation)", "owner": "C042-C046", "enforced_by": "C004 unavailability guard issues zero provider calls"}, {"id": "prior_record_farm_ownership", "path": "input.prior_records[].farm_id equals context.farm_id", "expect": "reject", "wire": "invalid_request", "owner": "C023", "enforced_by": "C004 binds the required per-record farm_id and executes a labelled cross-reference script; C023 owns typed farm_id equality with context.farm_id"}, - {"id": "proposal_expected_revision_linkage", "path": "output.proposed_changes[].expected_revision ties to the supplied target record when target.id is non-null", "expect": "reject", "wire": "invalid_request", "owner": "C023", "enforced_by": "C004 schema conditional (non-null id requires non-null expected_revision; id null forbids an invented revision) and a labelled cross-reference script; C023 owns typed linkage"} + {"id": "proposal_expected_revision_linkage", "path": "output.proposed_changes[].expected_revision ties to the supplied target record when target.id is non-null", "expect": "reject", "wire": "invalid_request", "owner": "C023", "enforced_by": "C004 schema conditional (non-null id requires non-null expected_revision; id null forbids an invented revision) and a labelled cross-reference script; C023 owns typed linkage"}, + {"id": "price_known_requires_scaled_facts", "path": "input.snapshots[].price state=known with null/absent amount, missing scale, decimal amount or out-of-range scale", "expect": "reject", "wire": "invalid_request", "owner": "C009", "enforced_by": "ADR-0027 D47 BP01 schema conditional (canonical nonnegative integer amount, integer scale 0..9, nonblank currency, closed basis); C009 typed scaled-money decode"}, + {"id": "price_unknown_is_not_zero", "path": "input.snapshots[].price state=unknown carrying a value, or unknown read as a free/zero price", "expect": "reject", "wire": "invalid_request", "owner": "C009", "enforced_by": "BP01 schema conditional (amount/scale/currency null and basis unknown); C009 maps the null wire fields mechanically to the existing explicit unknown alternative"}, + {"id": "price_coefficient_int64_bound", "path": "price amount coefficient beyond 9223372036854775807", "expect": "reject", "wire": "invalid_request", "owner": "C009", "enforced_by": "checked scale/int64 decode; the schema bounds only the canonical form and the 19-character magnitude"}, + {"id": "price_basis_closed", "path": "price basis outside per_unit|per_pack|per_order|unknown", "expect": "reject", "wire": "invalid_request", "owner": "C009", "enforced_by": "BP01 schema enum"}, + {"id": "price_fee_family_closed", "path": "price.fees entries not closed {kind,amount,scale,currency}", "expect": "reject", "wire": "invalid_request", "owner": "C009", "enforced_by": "BP01 schema; the legacy hyf_v1_jev money-schema fees family is retained unchanged as provenance"}, + {"id": "provenance_required_on_success", "path": "every successful output (including empty claims/lines and no-match/zero-plan results) without a closed provenance object", "expect": "reject", "wire": "invalid_request", "owner": "C009", "enforced_by": "BP01 response schema requires provenance on every success independently of context.return_provenance; C009 typed decode"}, + {"id": "provenance_identity_closed", "path": "provenance identity objects missing/blank revision or carrying unknown fields", "expect": "reject", "wire": "invalid_request", "owner": "C009/C023", "enforced_by": "BP01 closed identity objects with required nonblank identity strings; C023 equality"}, + {"id": "provenance_farm_prior_records_only", "path": "buyer interpretation provenance carrying the farm-only prior_records field", "expect": "reject", "wire": "invalid_request", "owner": "C023/C030", "enforced_by": "BP01 buyer provenance is closed without prior_records; C030 owns farm prior-record provenance"}, + {"id": "provenance_actual_use_only", "path": "source/clarification/prior_records/need/snapshot identities not actually consulted", "expect": "reject", "wire": "invalid_request", "owner": "C023/C041", "enforced_by": "C023/C041 semantic actual-use checks; BP03 executes the paired accepting/rejecting controls"}, + {"id": "provenance_repeated_identity_pair", "path": "repeated identical identity/revision pair in provenance", "expect": "reject", "wire": "invalid_request", "owner": "C023", "enforced_by": "C023 semantic uniqueness; BP03 executes the paired controls"}, + {"id": "provenance_snapshot_scope_consistency", "path": "match provenance snapshots disagree with assessed/allocated lot revisions or with supplied scope", "expect": "reject", "wire": "invalid_request", "owner": "C023/C041", "enforced_by": "C023/C041 semantic consistency with the already-bound snapshot consistency policy"} ] } diff --git a/schemas/hyf_ops_v2/version_manifest.json b/schemas/hyf_ops_v2/version_manifest.json @@ -2,7 +2,7 @@ "schema_version": 1, "program": "hyf_http_v1", "revision": "hyf_ops_v2", - "authority": "ADR-0025 D45 CB01-CB05 over ADR-0010 D21 (selector hyf_ops_v2) and ADR-0011 D23 (reported quantity grammar)", + "authority": "ADR-0025 D45 CB01-CB05 over ADR-0010 D21 (selector hyf_ops_v2) and ADR-0011 D23 (reported quantity grammar); ADR-0027 D47 BP01 binds the v2 snapshot.price scaled-money copy and required output provenance without changing these axes.", "note": "Mechanical C004 version manifest. These are the current checked-in identifiers for the seven closed context.versions axes. review_policy/ranking_policy/question_bundle identifier semantics are bound by C008/C009/C023 against the checked-in implementations; C004 records the contract-revision identifiers so the strict schemas, examples and parser are self-consistent. model is the configured pinned assisted model default.", "versions": { "schema": "hyf_ops_v2", diff --git a/tests/test_hyf.mojo b/tests/test_hyf.mojo @@ -1894,6 +1894,119 @@ def test_c004_v2_schema_assets_and_manifest_integrity() raises: assert_true(exists(schema_dir / entry["file"].string_value())) +def test_c004_bp01_money_and_provenance_bindings() raises: + # ADR-0027 D47 BP01: the v2 copy binds scaled money on snapshot.price and a + # required closed provenance object on every successful output. + var schema_dir = _dir_of_current_file() / ".." / "schemas" / "hyf_ops_v2" + var match_request = loads( + (schema_dir / "buyer_request_match.request.schema.json").read_text() + ) + var price = match_request["$defs"]["snapshot_price"] + var price_required = List[String]() + for value in price["required"].array_items(): + price_required.append(value.string_value()) + assert_equal(len(price_required), 5) + for field in ["state", "amount", "scale", "currency", "basis"]: + assert_true(field in price_required) + assert_equal(price["additionalProperties"].bool_value(), False) + assert_true(_has_key(price["properties"], "fees")) + assert_equal(len(price["allOf"].array_items()), 2) + + # price remains optional on the snapshot; the quantity fields stay required. + var snapshot_required = List[String]() + for value in match_request["$defs"]["snapshot"]["required"].array_items(): + snapshot_required.append(value.string_value()) + assert_true("lot_id" in snapshot_required) + assert_true(not ("price" in snapshot_required)) + + var farm_response = loads( + (schema_dir / "farm_update_interpret.response.schema.json").read_text() + ) + var buyer_response = loads( + ( + schema_dir / "buyer_request_interpret.response.schema.json" + ).read_text() + ) + var match_response = loads( + (schema_dir / "buyer_request_match.response.schema.json").read_text() + ) + var farm_required = List[String]() + for value in farm_response["$defs"]["farm_output"][ + "required" + ].array_items(): + farm_required.append(value.string_value()) + assert_true("provenance" in farm_required) + var buyer_required = List[String]() + for value in buyer_response["$defs"]["interpret_output"][ + "required" + ].array_items(): + buyer_required.append(value.string_value()) + assert_true("provenance" in buyer_required) + var match_required = List[String]() + for value in match_response["$defs"]["match_output"][ + "required" + ].array_items(): + match_required.append(value.string_value()) + assert_true("provenance" in match_required) + + # Farm provenance keeps the prior-record family; buyer provenance is closed + # without it; match provenance records need and evaluated snapshots. + assert_true( + _has_key( + farm_response["$defs"]["farm_output_provenance"]["properties"], + "prior_records", + ) + ) + assert_true( + not _has_key( + buyer_response["$defs"]["interpret_output_provenance"][ + "properties" + ], + "prior_records", + ) + ) + var match_provenance_required = List[String]() + for value in match_response["$defs"]["match_output_provenance"][ + "required" + ].array_items(): + match_provenance_required.append(value.string_value()) + assert_true("need" in match_provenance_required) + assert_true("snapshots" in match_provenance_required) + + var corpus = loads((schema_dir / "examples" / "corpus.json").read_text()) + var corpus_files = List[String]() + for entry in corpus["entries"].array_items(): + corpus_files.append(entry["file"].string_value()) + assert_true("examples/valid/price.known_zero.json" in corpus_files) + assert_true("examples/valid/price.unknown.json" in corpus_files) + assert_true("examples/invalid/price.adjacent_overflow.json" in corpus_files) + assert_true( + "examples/invalid/farm_update_interpret.response.missing_provenance.json" + in corpus_files + ) + + var semantic = loads((schema_dir / "semantic-cases.json").read_text()) + var case_ids = List[String]() + for semantic_case in semantic["cases"].array_items(): + case_ids.append(semantic_case["id"].string_value()) + for case_id in [ + "price_known_requires_scaled_facts", + "price_unknown_is_not_zero", + "price_coefficient_int64_bound", + "price_basis_closed", + "price_fee_family_closed", + "provenance_required_on_success", + "provenance_identity_closed", + "provenance_farm_prior_records_only", + ]: + assert_true(case_id in case_ids) + + # The manifest documents both bindings. + var manifest = loads((schema_dir / "manifest.json").read_text()) + assert_true(_has_key(manifest, "money_binding")) + assert_true(_has_key(manifest, "provenance_binding")) + + def test_c004_operation_v2_escaped_duplicate_key_is_rejected() raises: # \u0061 is 'a'; the parser decodes key escapes, so the second key is a # duplicate actor_id and must be rejected by decoded-key identity.