Summary
POST /api/v1/orders accepts any string as deal_id and persists the order without resolving the reference against stored deals. The sibling creation endpoint POST /api/v1/quotes does resolve its parent reference and returns a structured 404. A change request against such an order then inherits the unresolvable deal_id and reaches pending_approval at material severity, with validation_errors empty.
Observed — orders accept an unresolvable reference
curl -s -X POST localhost:8001/api/v1/orders \
-H "Content-Type: application/json" \
-d '{"deal_id":"does-not-exist-1787208750"}'
{
"order_id": "ORD-6D0336B37B8A",
"status": "draft",
"deal_id": "does-not-exist-1787208750",
"quote_id": null,
"created_at": "2026-08-20T06:52:30.485080Z"
}
HTTP 200, persisted. The order survived a server restart, so it is durable rather than a session artefact.
order_service.py:40-50 writes both references straight through:
order_data["deal_id"] = deal_id
order_data["quote_id"] = quote_id
There is no storage lookup for either.
Observed — the sibling endpoint does resolve its reference
curl -s -X POST localhost:8001/api/v1/quotes \
-H "Content-Type: application/json" \
-d '{"idempotency_key":"probe-key-0001","product_id":"does-not-exist-xyz","deal_type":"PG","impressions":1000000}'
{"detail":{"error":"product_not_found","message":"Product 'does-not-exist-xyz' not found in catalog."}}
HTTP 404, from quote_service.py:121-127.
Two creation endpoints on the same API, one resolving its parent reference and one not.
Observed — the reference propagates into approval
curl -s -X POST localhost:8001/api/v1/change-requests \
-H "Content-Type: application/json" \
-d '{"order_id":"ORD-6D0336B37B8A","change_type":"impressions","reason":"probe"}'
{
"change_request_id": "CR-14C50C574E82",
"order_id": "ORD-6D0336B37B8A",
"deal_id": "does-not-exist-1787208750",
"status": "pending_approval",
"severity": "material",
"validation_errors": []
}
A material change request carrying a deal reference this service cannot resolve, awaiting human approval, with the system reporting no validation errors.
Environment
seller-agent v2.4.2, commit e5b367d2b780aa4d0e03b6363d744ba3adf2b221. Also present on main, which is currently the same commit at time of reporting. Python 3.12.3, WSL2 Ubuntu 24.04.
Inferred
Whether orders are intended to precede deals. draft status, three optional fields and no validation all suggest an order may legitimately be opened before commercial terms are settled. If that is the design, requiring a resolvable deal_id at creation would break it. The narrower observation stands either way: an order opened without terms is a different thing from one carrying a deal reference the service cannot resolve. I have not established whether such a reference might be meaningful in an external commercial system; only that this service does not resolve it and does not surface that.
Reach beyond change requests. order["deal_id"] is also read by gam_reporting_service.py:54-62, where it is used to populate external_order_id when linking to Google Ad Manager. I have not exercised that path and make no claim about its behaviour — only that the unresolved reference is consumed by more than one downstream service.
quote_id is written identically and appears to be equally unvalidated. Not tested.
Relationship to #61
#61 concerns a null deal_id producing an unhandled 500 on the change-request path. This is the non-null but unresolvable case, which succeeds silently rather than crashing. Same non-validation in create_order, different failure mode, and the fix proposed on #61 — normalising None at write side — would not address it.
Suggested remedy
Not prescribed, since it turns on whether orders may precede deals.
If they may not, resolving deal_id at creation and returning a structured 404 for an unknown deal would mirror what /quotes already does for product_id.
If they may, then the reference is optional-but-must-be-real, and validation belongs wherever it is first relied upon — the change-request path being one such place, given the severity classification and approval routing that follow.
Acceptance criteria
- An order cannot come to carry a
deal_id this service cannot resolve, or the condition is detected before a change request against that order is classified and routed for approval.
- Whichever layer is chosen, a change request that inherits an unresolvable deal reference does not reach
pending_approval with validation_errors empty.
Summary
POST /api/v1/ordersaccepts any string asdeal_idand persists the order without resolving the reference against stored deals. The sibling creation endpointPOST /api/v1/quotesdoes resolve its parent reference and returns a structured 404. A change request against such an order then inherits the unresolvabledeal_idand reachespending_approvalatmaterialseverity, withvalidation_errorsempty.Observed — orders accept an unresolvable reference
{ "order_id": "ORD-6D0336B37B8A", "status": "draft", "deal_id": "does-not-exist-1787208750", "quote_id": null, "created_at": "2026-08-20T06:52:30.485080Z" }HTTP 200, persisted. The order survived a server restart, so it is durable rather than a session artefact.
order_service.py:40-50writes both references straight through:There is no storage lookup for either.
Observed — the sibling endpoint does resolve its reference
{"detail":{"error":"product_not_found","message":"Product 'does-not-exist-xyz' not found in catalog."}}HTTP 404, from
quote_service.py:121-127.Two creation endpoints on the same API, one resolving its parent reference and one not.
Observed — the reference propagates into approval
{ "change_request_id": "CR-14C50C574E82", "order_id": "ORD-6D0336B37B8A", "deal_id": "does-not-exist-1787208750", "status": "pending_approval", "severity": "material", "validation_errors": [] }A material change request carrying a deal reference this service cannot resolve, awaiting human approval, with the system reporting no validation errors.
Environment
seller-agent v2.4.2, commit
e5b367d2b780aa4d0e03b6363d744ba3adf2b221. Also present onmain, which is currently the same commit at time of reporting. Python 3.12.3, WSL2 Ubuntu 24.04.Inferred
Whether orders are intended to precede deals.
draftstatus, three optional fields and no validation all suggest an order may legitimately be opened before commercial terms are settled. If that is the design, requiring a resolvabledeal_idat creation would break it. The narrower observation stands either way: an order opened without terms is a different thing from one carrying a deal reference the service cannot resolve. I have not established whether such a reference might be meaningful in an external commercial system; only that this service does not resolve it and does not surface that.Reach beyond change requests.
order["deal_id"]is also read bygam_reporting_service.py:54-62, where it is used to populateexternal_order_idwhen linking to Google Ad Manager. I have not exercised that path and make no claim about its behaviour — only that the unresolved reference is consumed by more than one downstream service.quote_idis written identically and appears to be equally unvalidated. Not tested.Relationship to #61
#61 concerns a null
deal_idproducing an unhandled 500 on the change-request path. This is the non-null but unresolvable case, which succeeds silently rather than crashing. Same non-validation increate_order, different failure mode, and the fix proposed on #61 — normalisingNoneat write side — would not address it.Suggested remedy
Not prescribed, since it turns on whether orders may precede deals.
If they may not, resolving
deal_idat creation and returning a structured 404 for an unknown deal would mirror what/quotesalready does forproduct_id.If they may, then the reference is optional-but-must-be-real, and validation belongs wherever it is first relied upon — the change-request path being one such place, given the severity classification and approval routing that follow.
Acceptance criteria
deal_idthis service cannot resolve, or the condition is detected before a change request against that order is classified and routed for approval.pending_approvalwithvalidation_errorsempty.