Skip to content

feat(OMN-17304): RuntimeLocal client mode — publish for dispatch, host nothing, await THIS run's terminal - #1633

Merged
github-actions[bot] merged 1 commit into
devfrom
jonah/omn-17304-runtime-local-client-mode
Sep 1, 2026
Merged

github-actions[bot] merged 1 commit into
devfrom
jonah/omn-17304-runtime-local-client-mode

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

feat(OMN-17304): RuntimeLocal client mode — publish for dispatch, host nothing, await THIS run's terminal

Closes the execution-locus half of OMN-17304 (PR2) and supplies the runtime
primitive OMN-17295 AC1/AC2 need. Unblocked by omnibase_infra#3064 (the
OMN-17295 correlation join) merging to dev at 2026-08-31T12:26:48Z.

THE DEFECT

_run_event_driven was unconditionally BOTH roles. It subscribed every
handler_routing.handlers entry to its input_topic (:1215-1220 pre-change) and
then published the initial command to that same topic (:1300-1305). On a
shared broker that makes the caller a second, DISJOINT consumer group of the
topic it publishes to, so Kafka fans every command to both the deployed
runtime and the caller and both execute the entry handler.

Measured on the .201 dev lane, 2026-08-31T20:06Z, correlation
993819e0-f3e5-4064-a1dd-0b85ee263105:

lane group  local.omnimarket.node_delegate_skill_orchestrator...  151 -> 152
Mac CLI     local.omnibase_core.handlerdelegateskill...v1.__t...   53 ->  54
Mac CLI lag                                                              98

The CLI committed offset 54 — it consumed a 98-message-old backlog command,
not its own — while the lane consumed the real one. --bus kafka selects the
transport; it never relocated execution. Every acceptance criterion written
against that invocation measured the operator's laptop.

WHAT LANDS

  1. RuntimeLocal(host_handlers=...). True (default) is byte-for-byte today's
    behaviour and the offline/standalone path. False skips the
    handler-subscribe loop entirely and keeps publish + terminal await, so the
    work is done by whatever runtime is actually subscribed to the command
    topic.

  2. Correlation-scoped terminal await, armed only in client mode. Host mode on
    its own in-memory bus has exactly one producer of the terminal topic, so
    filtering there would change offline behaviour for no gain — a scope pinned
    by its own counter-test. Every correlation an envelope declares (envelope
    level AND payload level) must be this run's; an envelope naming nobody is
    unattributable and is discarded, matching the receipt-layer rule in
    omnibase_infra#3064.

  3. Run-scoped terminal consumer group in client mode (OMN-17304 AC5). The
    shared runtime-local group id is right for a long-lived host and wrong for
    a one-shot client, which would otherwise resume a previous run's committed
    offset and work through a backlog of terminals that are by construction not
    its own.

  4. AC6 — the published correlation is the reported correlation. The old path
    minted a uuid4, assigned it to payload.correlation_id, and swallowed the
    failure into "except (AttributeError, ValueError): pass".
    ModelDelegateSkillRequest is frozen, so on the delegation path that
    assignment ALWAYS raised and was ALWAYS discarded: the bytes on the wire
    correctly kept the caller's id while every "published initial command
    (correlation_id=...)" log line named the id just thrown away. The caller's
    id is now the authority, a runtime-minted id fills only a genuine absence,
    and a refusal to stamp one is reported rather than hidden. Client mode
    REFUSES to publish a command it cannot correlate — without an id on the
    wire the terminal watcher cannot tell this run's terminal from anyone
    else's, which is the failure this whole change exists to remove.

  5. handler_locus and wire_correlation_id in workflow_result.json, so a receipt
    built from that file can answer "did this process do the work" from the
    record instead of by hand-correlating container logs afterwards.

NAMING, DELIBERATELY

handler_locus, not execution_locus. OMN-17308 is in flight on
model_skill_result.py adding ModelRuntimeIdentity.execution_locus, which names
where THIS PROCESS runs (venv prefix or container id). Same word, different
question: that one answers "which install am I", this one answers "did I do
the work". No file overlaps between the two changes.

dispatched, not deployed_lane: the runtime knows it did not execute the work,
not who did. Asserting WHICH deployment answered is the caller's job, from a
lane-side fact.

RED-THEN-GREEN

tests/unit/runtime/test_runtime_local_client_mode.py — 10 tests, all failing
before the change (TypeError: RuntimeLocal.init() got an unexpected
keyword argument 'host_handlers'), all passing after. Four are host-mode
counter-assertions that pass BOTH before and after: without them, "never
subscribe anything" and "filter everything" would satisfy the positive
assertions while destroying the offline path.

Gates: ruff format + check clean; mypy --strict clean; pre-commit on the two
changed files 0 failed; pytest tests/unit/runtime/ 375 passed.


dod_evidence

Ticket: OMN-17304 (execution locus is a resolved property) — supplies the runtime primitive OMN-17295 AC1/AC2 need. Folded under OMN-17356 (D-1: a delegation runs on the deployed lane, and the receipt proves it did).

Claim Proof
The CLI was a second, disjoint consumer of the topic it published to .201 dev lane, correlation 993819e0-f3e5-4064-a1dd-0b85ee263105: lane group 151 → 152, Mac CLI group 53 → 54 at lag 98 — it consumed a 98-message-old backlog command, not its own
Client mode stops that, measured not asserted correlation f99d5485-1d18-476e-861b-0e81835752cf with --locus deployed-lane: Mac CLI group 54 → 54 UNCHANGED, lane group 152 → 153
The deployed orchestrator did the work docker logs omninode-runtime --since 20m | grep f99d5485 → 19 hits; omninode-runtime-effects → 7
The lane's own answer came back status: completed, qwen3.8 @ 192.168.86.201:8000, quality 1.0, 1 attempt, 0 escalations, cost_usd 0.0 — no metered provider
Host mode is byte-for-byte unchanged (offline/standalone path) 4 host-mode counter-assertions in the new suite pass BOTH before and after; live --bus inmemory probe returns status: completed on the local rung
AC6 — the published correlation is the one reported capture log now prints correlation_id=f99d5485-…, source=payload; previously it printed a discarded uuid4 because the frozen-model ValidationError was swallowed
RED-then-GREEN tests/unit/runtime/test_runtime_local_client_mode.py, 10 tests, all TypeError: RuntimeLocal.__init__() got an unexpected keyword argument 'host_handlers' before
Gates ruff format + check clean; mypy --strict clean; pre-commit on changed files 0 failed; pytest tests/unit/runtime/ 375 passed
Governed pre-push the selector escalated fail-closed to the full suite (is_full_suite=True reason=shared_module) and it was run on the designated gate host. No PREPUSH_* override, no --no-verify, no skip token

Downstream ordering

omnibase_infra PR jonah/omn-17304-delegate-lane-locus (--locus, the fail-closed dispatch gate, receipt provenance) requires this release. Against omnibase-core 0.47.1 RuntimeLocal.__init__() rejects host_handlers and every onex node receipt run fails. Sequence: this PR merges → core 0.47.2 to PyPI → infra pin bump → infra PR.

Evidence-Ticket: OMN-17304
Evidence-Source: OCC#7925

…t nothing, await THIS run's terminal

Closes the execution-locus half of OMN-17304 (PR2) and supplies the runtime
primitive OMN-17295 AC1/AC2 need. Unblocked by omnibase_infra#3064 (the
OMN-17295 correlation join) merging to dev at 2026-08-31T12:26:48Z.

THE DEFECT

_run_event_driven was unconditionally BOTH roles. It subscribed every
handler_routing.handlers entry to its input_topic (:1215-1220 pre-change) and
then published the initial command to that same topic (:1300-1305). On a
shared broker that makes the caller a second, DISJOINT consumer group of the
topic it publishes to, so Kafka fans every command to both the deployed
runtime and the caller and both execute the entry handler.

Measured on the .201 dev lane, 2026-08-31T20:06Z, correlation
993819e0-f3e5-4064-a1dd-0b85ee263105:

    lane group  local.omnimarket.node_delegate_skill_orchestrator...  151 -> 152
    Mac CLI     local.omnibase_core.handlerdelegateskill...v1.__t...   53 ->  54
    Mac CLI lag                                                              98

The CLI committed offset 54 — it consumed a 98-message-old backlog command,
not its own — while the lane consumed the real one. --bus kafka selects the
transport; it never relocated execution. Every acceptance criterion written
against that invocation measured the operator's laptop.

WHAT LANDS

1. RuntimeLocal(host_handlers=...). True (default) is byte-for-byte today's
   behaviour and the offline/standalone path. False skips the
   handler-subscribe loop entirely and keeps publish + terminal await, so the
   work is done by whatever runtime is actually subscribed to the command
   topic.

2. Correlation-scoped terminal await, armed only in client mode. Host mode on
   its own in-memory bus has exactly one producer of the terminal topic, so
   filtering there would change offline behaviour for no gain — a scope pinned
   by its own counter-test. Every correlation an envelope declares (envelope
   level AND payload level) must be this run's; an envelope naming nobody is
   unattributable and is discarded, matching the receipt-layer rule in
   omnibase_infra#3064.

3. Run-scoped terminal consumer group in client mode (OMN-17304 AC5). The
   shared runtime-local group id is right for a long-lived host and wrong for
   a one-shot client, which would otherwise resume a previous run's committed
   offset and work through a backlog of terminals that are by construction not
   its own.

4. AC6 — the published correlation is the reported correlation. The old path
   minted a uuid4, assigned it to payload.correlation_id, and swallowed the
   failure into "except (AttributeError, ValueError): pass".
   ModelDelegateSkillRequest is frozen, so on the delegation path that
   assignment ALWAYS raised and was ALWAYS discarded: the bytes on the wire
   correctly kept the caller's id while every "published initial command
   (correlation_id=...)" log line named the id just thrown away. The caller's
   id is now the authority, a runtime-minted id fills only a genuine absence,
   and a refusal to stamp one is reported rather than hidden. Client mode
   REFUSES to publish a command it cannot correlate — without an id on the
   wire the terminal watcher cannot tell this run's terminal from anyone
   else's, which is the failure this whole change exists to remove.

5. handler_locus and wire_correlation_id in workflow_result.json, so a receipt
   built from that file can answer "did this process do the work" from the
   record instead of by hand-correlating container logs afterwards.

NAMING, DELIBERATELY

handler_locus, not execution_locus. OMN-17308 is in flight on
model_skill_result.py adding ModelRuntimeIdentity.execution_locus, which names
where THIS PROCESS runs (venv prefix or container id). Same word, different
question: that one answers "which install am I", this one answers "did I do
the work". No file overlaps between the two changes.

dispatched, not deployed_lane: the runtime knows it did not execute the work,
not who did. Asserting WHICH deployment answered is the caller's job, from a
lane-side fact.

RED-THEN-GREEN

tests/unit/runtime/test_runtime_local_client_mode.py — 10 tests, all failing
before the change (TypeError: RuntimeLocal.__init__() got an unexpected
keyword argument 'host_handlers'), all passing after. Four are host-mode
counter-assertions that pass BOTH before and after: without them, "never
subscribe anything" and "filter everything" would satisfy the positive
assertions while destroying the offline path.

Gates: ruff format + check clean; mypy --strict clean; pre-commit on the two
changed files 0 failed; pytest tests/unit/runtime/ 375 passed.
async def test_client_mode_refuses_an_unattributable_command(tmp_path: Path) -> None:
"""No correlation on the wire ⇒ no correlated await ⇒ refuse, don't guess."""

class ModelNoCorrelation(BaseModel):
jonahgabriel pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 1, 2026
…#7925)

* evidence: OCC companion pass 1 for OmniNode-ai/omnibase_core#1633

* evidence: OCC companion self-bind for #7925

---------

Co-authored-by: node-occ-companion-effect <occ-companion-effect@omninode.ai>
@github-actions
github-actions Bot merged commit fc20d08 into dev Sep 1, 2026
193 of 197 checks passed
@github-actions
github-actions Bot deleted the jonah/omn-17304-runtime-local-client-mode branch September 1, 2026 04:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants