Skip to content

fix(OMN-18294): read the gateway's declared prompt ceiling and stop discarding its field-level errors - #2509

Merged
jonahgabriel merged 1 commit into
devfrom
jonah/omn-18294-cloud-prompt-limit-client
Sep 13, 2026
Merged

jonahgabriel merged 1 commit into
devfrom
jonah/omn-18294-cloud-prompt-limit-client

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Closes OMN-18294 (client half). The gateway half is omninode_infra#1418 and cites the same ticket.

What was wrong

Dogfood round 2 (2026-09-13) submitted a real 58,368-character standup prompt through onex cloud delegate against the onex-lab-dogfood tenant and got back a sentence it could do nothing with:

[ONEX_CORE_007_INVALID_INPUT] the gateway refused the request to submit
the delegation with 400. Gateway detail: payload does not conform to the
workflow contract

Two client-side defects sit behind that sentence.

The client discarded the answer it was given. This is the larger one. The gateway's 400 has always carried field-level errors merged into the body next to detail, and on this refusal that list said exactly what was wrong: {"field": "prompt", "message": "must be at most 65536 characters"}. _detail() read detail alone and threw the rest away. The generic sentence the customer saw was not the gateway being unhelpful; it was this client dropping the helpful half. Every field-level error is now rendered, and all of them at once, because fixing one violation and resubmitting to find the next is not a workflow to hand somebody. This is the half that makes every future contract violation legible, not just the one size rule this ticket happened to find.

The client had no way to know the limit before spending a round trip. submit now reads the ceiling from the gateway's own advertised contract, GET /v1/workflows/contracts, and refuses locally naming both the limit and the measured size. Neither number alone is actionable: the ceiling does not say how far over you are, and the size does not say what you broke.

What this deliberately does not do

It never hardcodes the limit. A client-side number the server never agreed to would be the same undeclared, unreadable limit this ticket exists to remove, relocated from the server to the laptop. A test advertises a ceiling of 1024 from the fake gateway and asserts the client obeys that, precisely so a future hardcoded constant cannot pass.

It never invents one when the gateway is silent. An older gateway, a 404, or a discovery outage all mean the ceiling is unknown. The submission then goes out and the gateway decides, which is correct: a default would refuse work a newer gateway would have accepted. Discovery is a legibility improvement, never a new dependency of submission, and two tests pin that.

It costs one request per CLI run, not one per submission. The gateway meters 30 requests/minute per tenant on the discovery plan and the poll loop already spends a good share of that budget, so the answer is cached per transport — the unknown answer included, or a gateway that has already said it cannot say would be re-asked on every submit.

Existing tests changed, and why

Three transport tests recorded every request as though it were the submission. They now answer the contract read separately and assert on the submission, which is what each was about. The submission's wire shape is unchanged and test_submission_wire_shape_is_unchanged pins it.

Tests

tests/unit/cloud/test_omn_18294_prompt_limit_client.py — 6 RED before this change, covering the local refusal, the contract-resolved ceiling, the discovery cache, and the two dropped-errors cases. 13 green after.

tests/unit/cloud/ + tests/unit/cli/test_cli_cloud.py: 81 passed. ruff check and ruff format clean. mypy --strict clean on the changed module.

Not covered

The lab readback is taken after merge on the rebuilt onex-lab lane, as one real onex cloud delegate of the same 57KB prompt as the dogfood tenant, and recorded on the ticket as dod_evidence. Until the gateway PR merges and the lane is rebuilt, this client will find no discovery endpoint on the lab and will defer to the gateway — which is the documented unknown-ceiling path, not a failure.

Evidence-Ticket: OMN-18294
Evidence-Source: OCC#9330

…discarding the gateway's own field errors

Dogfood round 2 submitted a real 58,368-character standup prompt through
`onex cloud delegate` and got back a sentence it could do nothing with:
"payload does not conform to the workflow contract". Two client-side defects
are behind that, and the second is the larger one.

**The client discarded the answer it was given.** The gateway's 400 has always
carried field-level `errors` next to `detail` — `{"field": "prompt",
"message": "must be at most 65536 characters"}`. `_detail()` read `detail`
alone. The generic sentence was not the gateway being unhelpful; it was this
client dropping the helpful half. Every field-level error is now rendered, all
of them at once, because fixing one violation and resubmitting to discover the
next is not a workflow to hand a customer. Malformed entries are skipped
rather than rendered — an exception raised while formatting a refusal would
replace it with a traceback.

**The client had no way to know the limit before spending a round trip.**
`submit` now reads the ceiling from the gateway's own advertised contract
(`GET /v1/workflows/contracts`, added in omninode_infra#1418) and refuses
locally, naming the limit AND the measured size. Neither number alone is
actionable.

The limit is never a constant in this repo. A client-side number the server
never agreed to is the same undeclared, unreadable limit this ticket exists to
remove, relocated from the server to the laptop. When the gateway does not
advertise one — an older gateway, a 404, a discovery outage — the ceiling is
unknown, the submission goes out, and the gateway decides. Discovery is a
legibility improvement, never a new dependency of submission.

Fetched at most once per transport and cached, unknown answers included: the
gateway meters 30 requests/minute per tenant on the discovery plan and the
poll loop already spends a chunk of that, so this costs one request per CLI
run rather than one per submission.

Three existing transport tests recorded every request as though it were the
submission; they now answer the contract read separately and assert on the
submission, which is what each was actually about. The submission's wire shape
is unchanged and a test pins that.

Tests: tests/unit/cloud/test_omn_18294_prompt_limit_client.py — 6 RED before
this change, 13 green after. tests/unit/cloud/ + tests/unit/cli/test_cli_cloud.py:
81 passed. ruff clean; mypy --strict clean on the changed module.
@github-actions

Copy link
Copy Markdown

✅ Architectural Review — PASSED

Errors: 0
Warnings: 0

What this checks

Rule Description
ARCH-TOPIC-001 No hardcoded Kafka topic strings in handler code
ARCH-DI-001 No event_bus=None bypass
ARCH-DI-002 No direct Handler instantiation outside workflow_runner/adapters
ARCH-DI-003 No reinvented DI containers
ARCH-TOPIC-002 contract.yaml topics follow `onex.{cmd

No architectural violations found.

Static architectural lint — no model inference (OMN-14176).

@github-actions

Copy link
Copy Markdown

✅ Hostile Reviewer — PASSED

Blocking findings (critical/error): 0
Total findings: 0
Models succeeded: qwen3-review,qwen3-review-b


Gate semantics

Verdict Meaning Blocks merge?
passed >=2 models succeeded, no critical/error findings No
blocked CRITICAL or ERROR findings found Yes
degraded Fewer than 2 models succeeded (infra unavailable/timeout) Yes (OMN-15110)

Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524)

onexbot-occ-writer Bot added a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 13, 2026
…nimarket#2509

The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited.

Ticket: OMN-16859 (AC3b)
@jonahgabriel
jonahgabriel enabled auto-merge (squash) September 13, 2026 10:53
jonahgabriel pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 13, 2026
…2509

OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head b2fec7f3a59454006ebfcae2e3b25df2b3f5e153.
jonahgabriel pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 13, 2026
…nimarket#2509

The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited.

Ticket: OMN-16859 (AC3b)
jonahgabriel added a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 13, 2026
…imarket#2509 (#9330)

* evidence(OMN-18294): author OCC companion for OmniNode-ai/omnimarket#2509

OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head b2fec7f3a59454006ebfcae2e3b25df2b3f5e153.

* evidence(OMN-18294): self-bind OCC#9330 + rebind contract_sha256

* evidence(OMN-16859): executed test_passes receipts for OmniNode-ai/omnimarket#2509

The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited.

Ticket: OMN-16859 (AC3b)

* evidence(OMN-18294): author OCC companion for OmniNode-ai/omnimarket#2509

OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head b2fec7f3a59454006ebfcae2e3b25df2b3f5e153.

* evidence(OMN-16859): executed test_passes receipts for OmniNode-ai/omnimarket#2509

The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited.

Ticket: OMN-16859 (AC3b)

---------

Co-authored-by: omnimarket-bot <bot@omninode.ai>
Co-authored-by: onexbot-occ-writer[bot] <onexbot-occ-writer[bot]@users.noreply.github.com>
Co-authored-by: jonahgabriel <jonah@omninode.ai>
@jonahgabriel
jonahgabriel merged commit c75f02f into dev Sep 13, 2026
197 of 209 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-18294-cloud-prompt-limit-client branch September 13, 2026 13:13
Patel230 pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 16, 2026
…imarket#2509 (#9330)

* evidence(OMN-18294): author OCC companion for OmniNode-ai/omnimarket#2509

OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head b2fec7f3a59454006ebfcae2e3b25df2b3f5e153.

* evidence(OMN-18294): self-bind OCC#9330 + rebind contract_sha256

* evidence(OMN-16859): executed test_passes receipts for OmniNode-ai/omnimarket#2509

The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited.

Ticket: OMN-16859 (AC3b)

* evidence(OMN-18294): author OCC companion for OmniNode-ai/omnimarket#2509

OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head b2fec7f3a59454006ebfcae2e3b25df2b3f5e153.

* evidence(OMN-16859): executed test_passes receipts for OmniNode-ai/omnimarket#2509

The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited.

Ticket: OMN-16859 (AC3b)

---------

Co-authored-by: omnimarket-bot <bot@omninode.ai>
Co-authored-by: onexbot-occ-writer[bot] <onexbot-occ-writer[bot]@users.noreply.github.com>
Co-authored-by: jonahgabriel <jonah@omninode.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant