Skip to content

fix(OMN-16249): match the projection injected-key seam across the infra/market boundary - #2109

Merged
github-actions[bot] merged 1 commit into
devfrom
jonah/omn-16249-projection-injected-key-seam
Aug 19, 2026
Merged

github-actions[bot] merged 1 commit into
devfrom
jonah/omn-16249-projection-injected-key-seam

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes OMN-16249. The live canary on onex-dev submitted a real batch through the deployed gateway (POST /v1/workflows, workflow_type: hook-event-capture), got HTTP 202, the consumer received the message — and ModelHookEventCaptureRequest then rejected it with a pydantic extra_forbidden on _envelope_id. DLQ'd, zero rows, nothing in the 202 telling the caller.

Adjudication — both of the ticket's hypotheses are wrong

The ticket asked whether this is a gateway defect or a wiring bug local to hook-event-capture. It is neither.

The gateway does not inject _envelope_id. docker/onex-api/ contains zero occurrences of that token. Its wire envelope names the field envelope_id, no leading underscore, built in to_canonical_wire_envelope (omninode_infra docker/onex-api/models/model_workflow_envelope.py:222) and published from routers/workflows.py:475.

The sole producer of the underscore key is the runtime, consumer-side, after deserialization. omnibase_infra's projection dispatch arm injects four bookkeeping keys into the plain dict it hands handle():

Key Type Site Conditional?
_db ProjectionDatabaseOperations handler_wiring.py:3087 no
_event_type str handler_wiring.py:3088 no
_topic str handler_wiring.py:3089 no
_envelope_id uuid.UUID handler_wiring.py:3091-3095 yes — only when _extract_projection_envelope_id() coerces a UUID

That is a platform convention, not a defect — handler_wiring.py:3092-3094 documents _envelope_id as a durable idempotency key for redeliveries.

The actual defect is that the convention is enumerated independently on both sides of a repo boundary. node_hook_event_capture declares db_io.db_tables, so wire_from_manifest routes it through that arm, and its handler correctly strips injected keys via this repo's canonical shim. But split_projection_input's _RUNTIME_INJECTED_KEYS enumerated three keys while the producer injects four. _envelope_id survived into model_payload and detonated against extra="forbid".

Why it hid. Injection is conditional. Reducers fed by internal events without envelope identity never saw the key, so all six split_projection_input callers looked healthy. Gateway-published envelopes always carry one, so the first node on that path failed on its first real message. This was latent for every strict-model projection handler in this repo, not specific to hook-event-capture.

None of the three forbidden shapes were used: the request model is unchanged, no extra tolerance was added anywhere, and there is no gateway special-case.

Seams matched field-by-field (OMN-14208)

Seam 1 — runtime → projection handle() argument. Positional dict[str, object]. Producer key set as tabled above; consumer contract is RUNTIME_INJECTED_KEYS in src/omnimarket/projection/handler_shim.py, which must be equal to it — a superset silently eats legitimate underscore-aliased domain fields (_runtime_backend, OMN-13825), a subset is this bug. Both directions are asserted.

Seam 2 — split_projection_input return triple. (db: DatabaseAdapter, model_payload: dict, injected_meta: dict). model_payload = input minus RUNTIME_INJECTED_KEYS, safe to splat into a strict model. injected_meta = RUNTIME_INJECTED_KEYS - {_db}, now carrying _envelope_id so reducers keep the idempotency-key capability the producer injects it for rather than having it dropped on the floor.

Seam 3 — batch payload → ModelHookEventCaptureRequest (frozen, extra="forbid"): source, batch_sha, events[], correlation_id, emitted_at, tenant_id, tenant_principal_id. Unchanged by this PR; the test fixture is built field-for-field against it.

The durable half: a fail-closed drift guard

Widening a frozenset fixes today's break and nothing else — the seam would drift again the next time the producer grows a key. tests/test_omn16249_projection_injected_key_seam.py therefore executes the installed omnibase_infra dispatch callback and harvests what it actually injects, rather than restating the key set. The next upstream key lands as a red test in this repo's own CI, with no cross-repo release coordination needed to detect it. A static cross-check fails closed if an upstream refactor ever makes the harvest stop observing the injection path, so a shape change is loud rather than a vacuous pass.

Test plan

Cross-boundary, driving the real seam — the producer side is the executed omnibase_infra callback, the consumer side is the real handler, real shim, and real extra="forbid" model. Not two independent doubles.

  • RED before / GREEN after, verified by reverting only the behavioural half of the fix (key set) while keeping the new export name, so the proof isolates behaviour from rename. RED reproduced the live symptom exactly: 1 validation error for ModelHookEventCaptureRequest / _envelope_id / Extra inputs are not permitted [type=extra_forbidden].
  • tests/test_omn16249_projection_injected_key_seam.py + tests/test_projection_handler_shim_omn13825.py — 19 passed on the .200 gate host.
  • Affected projection + node surface on .200 — 1590 passed, 30 skipped, 0 failed.
  • mypy src/omnimarket/ --strict — clean, 3028 source files.
  • ruff format --check + ruff check clean; full pre-commit hook chain passed on commit.

One test defect was caught by running on the clean gate host rather than this workstation: the harvest originally inherited an ambient OMNIDASH_ANALYTICS_DB_URL, so it passed on a developer Mac and failed on .200 where the binding DSNs are unset. The suite now sets them explicitly, and was re-verified locally with those vars cleared.

Refs OMN-16249.
Evidence-Ticket: OMN-16249

Evidence-Source: OCC#6725

The gateway does not inject `_envelope_id`. Its wire envelope names that
field `envelope_id`, no underscore (onex-api
models/model_workflow_envelope.py:222, to_canonical_wire_envelope). The
sole producer of the underscore key is omnibase_infra's projection
dispatch arm, handler_wiring._make_projection_dispatch_callback, which
injects `_db`/`_event_type`/`_topic`/`_envelope_id` into the plain dict it
hands handle(). That is a platform convention, not a defect.

The defect is that the convention is enumerated independently on both
sides of a repo boundary: the producer grew a fourth key, and this repo's
canonical stripper still enumerated three. `_envelope_id` therefore
survived into the payload and failed extra_forbidden against
ModelHookEventCaptureRequest -- 202 accepted, DLQ'd, zero rows.

It hid because the producer injects the key only when the envelope
carries a coercible envelope_id. Reducers fed by internal events never
saw it; gateway-published envelopes always carry one, so
node_hook_event_capture failed on its first real message.

Widens RUNTIME_INJECTED_KEYS to cover it and returns it in injected_meta
rather than dropping it, preserving the idempotency-key capability the
producer injects it for.

The durable half is the drift guard: tests harvest the key set by
EXECUTING the installed producer and fail closed when this repo's set
does not cover it, so the next upstream key lands as a red test here with
no cross-repo release coordination needed to detect it.

Refs OMN-16249.
@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Important

Review skipped

This review includes 2 billable files. This on-demand review is free during your promotion.

Your included review limit has been reached. Run @coderabbitai review --use-credits to review the latest changes using usage credits.

  • Run review — free
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f0784d2-ebd9-459f-9fae-3b3842a2abdb

📥 Commits

Reviewing files that changed from the base of the PR and between f09fd07 and 8b66eaa.

📒 Files selected for processing (2)
  • src/omnimarket/projection/handler_shim.py
  • tests/test_omn16249_projection_injected_key_seam.py

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Comment @coderabbitai help to get the list of available commands.

@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
github-actions Bot enabled auto-merge (squash) August 19, 2026 08:00
@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)

jonahgabriel added a commit to OmniNode-ai/onex_change_control that referenced this pull request Aug 19, 2026
…6725)

* evidence(OMN-16249): OCC companion for OmniNode-ai/omnimarket#2109

OCC companion contract + receipts for OmniNode-ai/omnimarket PR #2109 (matches
the projection injected-key seam across the infra/market boundary). No OCC
companion or contract existed for this ticket.

Net-new contract `contracts/OMN-16249.yaml` + 4 PASS receipts:
- dod-OmniNode-ai-omnimarket-pr-2109 -- live PR existence/head/scope probe
- dod-omn16249-producer-injected-keys -- omnibase_infra's handler_wiring.py
  at the pinned rev injects exactly the 4 documented keys (_db, _event_type,
  _topic, _envelope_id), verified via the GitHub contents API against the
  immutable ref
- dod-omn16249-consumer-widened-key-set -- omnimarket's handler_shim.py at
  the PR head declares RUNTIME_INJECTED_KEYS as the matching 4-key frozenset
- dod-omn16249-fail-closed-drift-guard-and-tests -- differential proof,
  live-executed on the .200 gate host: the seam regression suite (19 passed)
  and an independent live introspection of the installed omnibase_infra
  module reproducing the same 4-key set

Self-bind entry to follow in a second commit once this PR's number is known
(established pattern -- see OCC#6677/OMN-16170, OCC#6702/OMN-16089).

Cites: OMN-16249

* evidence(OMN-16249): self-bind OCC companion PR #6723

Appends occ-self-bind-pr-6723 to contracts/OMN-16249.yaml + its PASS receipt,
binding this companion to its own OCC PR number now that it is known. The
four prior dod_evidence entries are unchanged (append-only, per-entry hash
verified stable).

Cites: OMN-16249

* evidence(OMN-16249): correct fabricated commit_sha in the self-bind receipt

The self-bind receipt recorded commit_sha 3f35bfa939ddb6da... which does
not exist in this repository — 'git cat-file -t' fails on it. It shares
only the first 10 hex characters with the real companion commit
(3f35bfa...), a prefix match that cannot occur by chance, so the
value was a real short prefix with an invented remainder rather than a
stale or mis-copied SHA.

A receipt asserting PASS against a commit that does not exist is exactly
the class of unverifiable evidence receipts exist to prevent. Corrected to
the actual parent commit and re-verified with git cat-file.

The other four receipts' cross-repo SHAs were audited and are genuine:
8b66eaa3... is omnimarket#2109's real head, and 6fdbba6d... is a real
omnibase_infra commit (the OMN-14498 #2672 merge).

Refs OMN-16249.

* evidence(OMN-16249): rebind self-bind receipt to OCC#6725

PR #6723's CI run never materialized into jobs — status pending, zero
jobs, updated_at frozen at creation, reproduced across three head SHAs
and four re-trigger attempts while other OCC PRs expanded 30 jobs
normally. The stall was bound to that PR's identity, so this branch is
opened as a fresh PR rather than retried a fifth time.

Rebinds the self-bind evidence item and receipt from #6723 to #6725 and
recomputes contract_entry_sha256 for the changed entry. The probe was
re-executed against the new PR, not edited: 'gh pr view 6725 --json
number,state' returns {"number":6725,"state":"OPEN"} verbatim.

All five receipts re-verified against their contract entries with
omnibase_core compute_contract_entry_sha256 — all match.

Refs OMN-16249.

* evidence(OMN-16249): fix the two Contract Compliance findings on #6725

Contract Compliance Check reported 3/5 PASS, 1 WARN, 1 BLOCK. Both
findings are this companion's own content, not repo-wide noise, so both
are fixed here rather than waived.

BLOCK -- dod-omn16249-producer-injected-keys. The probe fetched
handler_wiring.py through the contents API's default JSON+base64
representation and died mid-transfer: "stream error: stream ID 1;
CANCEL; received from peer" after ~15s. That file is 371,665 bytes,
which base64 inflates to ~495KB wrapped in a JSON string. Switched to
"Accept: application/vnd.github.raw", which streams the file directly
and completes in well under a second. Re-run verbatim: exit 0, output 4.
The match pattern now anchors on the assignment operator so it counts
injection SITES rather than incidental mentions of the same keys.

WARN -- occ-self-bind-pr-6725. The checker classified the probe INERT:
"no admissible probe in command position ... proves nothing", because a
bare gh pr view is not one of the admissible probe verbs. Switched to
gh api, which is admissible and asserts the same fact against the live
API. Re-run verbatim: exit 0, output "6725 open".

Both probes were re-executed by this lane, not edited to match a desired
result, and the runner field now names the lane that actually ran them.
All five contract_entry_sha256 values recomputed and verified;
validate-yaml passes.

The producer description is a literal block scalar, not folded: the
OMN-15479 yamlfmt-contamination gate correctly rejected the folded form
once the description carried an internal newline.

The remaining reds (no-noncanonical-lifecycle-classes, reason-graph,
tests+coverage shadow) sit outside CI Summary's gating set and are
shared with the unrelated #6722, so they are repo-wide.

Refs OMN-16249.
@github-actions
github-actions Bot merged commit 1106a25 into dev Aug 19, 2026
178 of 189 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-16249-projection-injected-key-seam branch August 25, 2026 18:15
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