Repository navigation
fix(OMN-15660): run-scope the handler consumer group and refuse a sibling invocation's handler input - #1671
Merged
Conversation
…ling invocation's handler input AC3's handler half and AC2's handler half — the two the terminal-leg fix (#1645) did not touch. runtime_local.py subscribed to the command topic with derive_runtime_local_group_id(entry.handler_name), a group id identical for every invocation of the same handler, so two concurrent invocations shared one group and the broker handed each command to exactly one of them: a run's own work executed in the sibling's process. LocalRuntimeBusAdapter.on_message read correlation_id for logging only, with no conditional between deserialize and invoke, so nothing refused a foreign record either. The subscribe group is now suffixed with the invocation's run_id, and the adapter refuses a message declaring a correlation that is not this invocation's. Both are gated on the correlation predicate being armed, for the reason the terminal group already documents: a brand-new group reads the retained log from the beginning, so scoping without a filter trades the uncommitted tail for the entire retention window. A message declaring NO correlation is still invoked on — the lesson #1655 paid for on the terminal leg, since the canonical def-B message is a bare domain model with no field to carry one. A refusal is not routed through on_error: a sibling's record is not this run's failure, and failing on one would make every concurrent invocation fail the moment a sibling published. RED control (source stashed, tests kept): 9 failed / 14 passed. GREEN: 23 passed. Version bumped to 0.47.7 — packaged source changed on an already-published 0.47.6.
Contributor
|
No OCC evidence companion was minted for this PR. this PR is already bound to OCC#8804; its evidence companion exists and nothing needs authoring To clear this: Nothing to do — the companion already exists. Reported by |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
OMN-15660 — the handler leg: run-scope the command-topic group and refuse a sibling's handler input
omnibase_core#1645closed this ticket's terminal leg. AC3 names two callsites and says in its own sentence that a fix that scopes only one path is
incomplete. This closes the handler leg, and with it AC1, AC2 and AC3.
What was still open at
origin/dev09fdb3f7runtime_local.py:1562)#1645_terminal_correlation_matches)#1645, corrected by#1655runtime_local.py:1526)derive_runtime_local_group_id(entry.handler_name)— identical for every invocation of the same handlerLocalRuntimeBusAdapter.on_message)correlation_idread for logging only; no conditional between deserialize and invokeTwo concurrent invocations therefore joined one group on the command topic.
A broker hands a record to exactly one member of a group, so a run's own command
was executed in the sibling's process, and nothing on the receiving side refused
it.
The change
runtime_local.pysuffixes the handler subscribe group with the invocation'srun_id, using the shape already eight lines below it at:1555-1562.LocalRuntimeBusAdaptertakesexpected_correlation_idand refuses a messagedeclaring a correlation that is not this invocation's, before the handler
invoke.
Both are gated on the correlation predicate being armed, for the reason the
terminal group already documents: a brand-new group reads the retained log from
the beginning, so scoping without a filter trades "the uncommitted tail" for
"the entire retention window" — strictly worse. Neither half is sufficient
alone; that is AC3's point.
What this deliberately does NOT do
def-B message is the handler's bare domain model, which has no field in which
to carry a correlation. This is the lesson
#1655paid for on the terminalleg; refusing that shape would stall every def-B chain after its first hop
rather than isolating anything.
on_error. A sibling's record is not thisrun's failure; failing on one would make every concurrent invocation fail the
moment a sibling published.
byte-for-byte unchanged.
Each of the three is pinned by its own counter-assertion, so "refuse everything"
does not pass this suite.
Evidence
test_a_concurrent_run_never_executes_a_siblings_commandfailed with "one process executed both commands";test_host_mode_handler_group_is_run_scopedandtest_two_concurrent_host_runs_do_not_share_a_handler_groupfailed on the shared group idlocal.omnibase_core.handlerisolationecho.consume.v1uv run pytest tests/unit/runtime/test_runtime_local_adapter_correlation_refusal.py tests/unit/runtime/test_runtime_local_terminal_isolation.py -q→ 23 passedtest_runtime_local.py,test_runtime_local_client_mode.py,test_runtime_local_adapter_fanout.py,test_runtime_local_adapter_event_type.py,test_runtime_dispatch.py→ 99 passedruff format,ruff check --fix,mypy ... --strict→ clean, "no issues found in 2 source files"e60465b328d1124ff247224d29516e7183aa38b9(5684s)RuntimeLocalis an in-process library path, exercised by the suite above, not by a deployed lane. No lane was mutated by this work.The RED control for the new adapter test file is a
TypeErroron theconstructor kwarg rather than a behavioural failure, stated rather than glossed:
the refusal did not exist to fail. The behavioural RED lives in the three
runtime-level tests above, which run the real
RuntimeLocalagainst a durablebroker double.
pre-commit run --all-filesreports three failures on files this PR does nottouch (
util_topic_suffix.py,harness/harness_topics.py,node_no_env_fallbacks_check_compute/*,model_llm_route_resolved_event.py).Positive control: all three reproduce byte-identically on a clean
origin/devtree with this branch's changes stashed. The staged-file run at commit time is
clean.
Deploy surface
Library code under
src/omnibase_core/runtime/. No deployment, no contractschema change, no wire-format change. Version bumped to
0.47.7becausepackaged source changed on an already-published
0.47.6.Ticket: OMN-15660
Evidence-Source: OCC#8804
Evidence-Ticket: OMN-15660