Skip to content

fix(OMN-20299): onex delegate derives a caller lane when no rule names one, so no CLI run writes a null caller_lane - #4427

Merged
jonahgabriel merged 3 commits into
devfrom
jonah/omn-20299-caller-lane-never-null
Oct 1, 2026
Merged

jonahgabriel merged 3 commits into
devfrom
jonah/omn-20299-caller-lane-never-null

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

OMN-20299: onex delegate never sends a run with no caller lane

On the .201 dev lane (compose project omnibase-infra, delegation_events, read-only), 651 of 699 rows in the 24 hours to 2026-10-01T12:24Z had a null caller_lane. The largest groups all came through onex delegate: the worktree-prune classifier and triage (launchd jobs), the spread and liveness probes, CI-red classifiers, and Claude Code sessions that have a session id but no lane variable (this orchestrator's own session, 946b3f5f, among them). The resolver from OMN-19860 ended in "otherwise none", so each of those runs attributed to nobody.

OMN-20288 makes each internal caller pass --caller-lane. This PR is the resolver side, so that a caller that forgets is still named and the projection's null caller_lane means "did not come through this CLI".

What changes

cli/delegate_caller.py: after --caller-lane, the lane variables and the registered worktree, the lane is derived, most explicit first:

  1. launchd:<label> from XPC_SERVICE_NAME when it holds a launchd job label (Terminal and app sessions carry 0 or an application. name there and are ignored);
  2. gha:<repo>:<workflow> under GitHub Actions;
  3. session:<uuid> from the Claude Code session;
  4. unattributed:<host>.

Each derived name has a kind prefix no ledger lane uses, so it never counts as a lane's own delegation in the OMN-20299 share. The stderr caller: line names the rule (fallback env XPC_SERVICE_NAME, fallback session, fallback host). The model docstring now says the resolver never leaves the lane None.

Tests

  • New tests/unit/cli/test_cli_delegate_caller_never_null.py (16 cases). It was committed first, RED (16 failed on the parent commit), then the fix (all pass).
  • Two tests in test_cli_delegate_caller_identity.py and four in tests/integration/cli/test_delegate_ticket_cli_omn19514.py asserted the old "no lane" shape and now assert the derived lane. The ticket key assertions are unchanged.
  • pytest tests/unit/cli tests/integration/cli/test_delegate_ticket_cli_omn19514.py -k "caller or ticket": 85 passed. ruff format and check clean, mypy --strict clean on the changed modules, pre-commit over the branch range clean.

Lab proof (rule 24), .201 dev lane, compose project omnibase-infra

This branch's src placed ahead of the dispatch venv's install (omnibase_infra=0.38.62@shadowed in the identity line), onex delegate "... (OMN-20299 lab proof, caller fallback ...)" --task-type summarization --ticket OMN-20299 --bus kafka --lane dev --locus deployed-lane, no lane flag and no lane variable:

Run Caller line delegation_events row (read-only psql)
correlation fdffa5ba-0edb-4d18-aca9-29f4a0c24134, session set lane=session:946b3f5f-... (fallback session) caller_lane=session:946b3f5f-1b49-45bf-bb3d-a15f6158685f, local-heavy-reasoning on .201
correlation 2c50482d-07d2-4c13-bdcc-0bdb3d7d124c, session set same same lane, local-omnipc2-chat on .202
correlation 8df71f78-473f-4e82-b290-679a863c9168, session unset lane=unattributed:stickybeatz-studio.local (fallback host) caller_lane=unattributed:stickybeatz-studio.local, session empty

The released consumer accepted the metadata; no omnimarket change is needed.

Runtime-affecting: no. The CLI runs on the caller's host, not in a lane container.

Ticket: OMN-20299

Evidence-Ticket: OMN-20299
Evidence-Source: OCC#12145

Lab pool readback (OMN-18893)

LAB PROOF PASS: omnibase_infra#4427 head 7d0b58fdcf on lab-101 (192.168.86.101), isolated project omnibase-infra-local, 2026-10-01T14:06:00Z to 2026-10-01T14:19:09Z
  ok   head_matches
  ok   stack_built
  ok   image_identity
  ok   health_8085
  ok   health_8086
  ok   migration_gate_healthy
  ok   no_wiring_failures
  ok   focused_tests
  restored=yes (containers=0 volumes=0 networks=0 images=0 listeners=0 workdir=gone, snapshot-diff=0)
  method: scripts/runtime_build/prepr_runtime_pool.py (OMN-18893)
  receipt OmniNode-ai/omnibase_infra#4427@7d0b58fdcf84f11d78f2027c0a074563a6b3baf4:omnibase_infra.runtime_and_scripts@1 result=PASS verifier=MISSING_MANDATORY_CHECK (mandatory passing check(s) missing: ['changed_path_live', 'forwarder_refused_topic']) file=/Users/jonah/.local/state/omni/landing/lab/workers/000040/lab-proof-receipts/omnibase_infra-4427-7d0b58fdcf84f11d78f2027c0a074563a6b3baf4.receipt.json event=/Users/jonah/.local/state/omni/landing/lab/workers/000040/lab-proof-receipts/omnibase_infra-4427-7d0b58fdcf84f11d78f2027c0a074563a6b3baf4.event.json
  ledger: RELEASE re=<your HOLD id> surface=101-runtime result=PASS restored=yes

@jonahgabriel
jonahgabriel enabled auto-merge October 1, 2026 12:56
jonahgabriel pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Oct 1, 2026
…12145)

* evidence(OMN-20299): author OCC companion for OmniNode-ai/omnibase_infra#4427

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

* evidence(OMN-20299): self-bind OCC#12145 + rebind contract_sha256

---------

Co-authored-by: omnimarket-bot <bot@omninode.ai>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hostile Reviewer — adversarial findings (OMN-17492)

Models succeeded: qwen3-review, gpt-oss-review
Models failed: none
New finding threads: 0
Deduped (already posted on this PR): 0
Below quorum (one model only, reported not threaded): 12
Nit-level findings suppressed: 0

The model is the FINDER, never the gate: merge is gated only by the
deterministic Hostile Review Thread Gate, which blocks while
hostile-reviewer threads are unresolved. Resolve each thread after
addressing (or rejecting, with a reply) its finding.

Below quorum: 12 finding(s) raised by one model only (OMN-18479)

These are reported and NOT dropped, but they get no thread and do not block: a single model's finding no other model reproduced is not evidence enough to stop a merge. Read them; act on them if they are right.

  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py, _fallback_lane (qwen3-review) — session fallback emits raw UUID without slug sanitization | The session branch of _fallback_lane returns f"session:{session}" directly, bypassing slug. The session value is read from the CLAUDE_CODE
  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py, _derived and _fallback_lane launchd branch (qwen3-review) — Truncation to _LANE_MAX can produce a token that fails the lane regex | _derived and the launchd branch both apply [:_LANE_MAX] after joining. If the truncation point falls on a colon or a hyphen, the
  • [MAJOR] CLI payload generation (not shown in diff) needs modification (gpt-oss-review) — Derived caller lane never added to payload metadata | Integration tests now assert that the metadata map contains a "caller_lane" key, but the CLI code that builds the payload does not insert this fie
  • [MAJOR] src/omnibase_infra/cli/model_delegate_caller.py (gpt-oss-review) — Inconsistent optional type for ModelDelegateCaller.lane | The field lane is typed as Optional[str] with a default of None, yet documentation and resolver guarantee a non‑null value. This mismatch can
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py, _fallback_lane (qwen3-review) — XPC_SERVICE_NAME is attacker-controllable in a CI or container context | The launchd fallback reads XPC_SERVICE_NAME from the process environment. In a GitHub Actions runner, a Docker container, or an
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py, _fallback_lane GHA branch (qwen3-review) — GITHUB_REPOSITORY rsplit takes the last path segment, not the repo name | The GHA branch extracts the repository name via environ.get("GITHUB_REPOSITORY", "").strip().rsplit("/", 1)[-1]. GITHUB_REPOSI
  • [MINOR] tests/unit/cli/test_cli_delegate_caller_never_null.py (qwen3-review) — No test exercises the _LANE_MAX truncation boundary | The test suite parametrizes host values including 'a' * 300, which triggers truncation in the unattributed branch. However, no test feeds a launch
  • [MINOR] tests/integration/cli/test_delegate_ticket_cli_omn19514.py (qwen3-review) — Integration tests assert exact metadata key set, coupling to implementation detail | The integration tests now assert set(metadata) == {DELEGATE_TICKET_METADATA_KEY, "caller_lane"}. This hard-codes th
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py, resolve_delegate_caller (qwen3-review) — socket.gethostname() called at resolution time is not injectable in all call sites | resolve_delegate_caller accepts a host parameter that defaults to None, in which case socket.gethostname() is calle
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py:line where socket.gethostname() is used (gpt-oss-review) — Hostname leakage through caller_lane metadata | The fallback lane includes the machine hostname obtained via socket.gethostname(). When the caller_lane is added to request metadata, the internal host
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py: within _fallback_lane host resolution (gpt-oss-review) — Uncaught OSError from socket.gethostname() | socket.gethostname() can raise OSError on some platforms (e.g., when the hostname cannot be determined). The resolver does not handle this exception, causi
  • [MINOR] tests/unit/cli/test_cli_delegate_caller_identity.py (gpt-oss-review) — Non‑deterministic host fallback in tests without explicit host argument | Tests that invoke resolve_delegate_caller without providing the host argument rely on the default socket.gethostname(), which

@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

✅ Hostile Reviewer — REVIEWED

Critical findings: 0
Major findings: 5
Total findings: 11
Models succeeded: qwen3-review,gpt-oss-review
Models unavailable: none

Action required: findings were posted as review threads. Address or reject each one, then resolve its thread — the Hostile Review Thread Gate fails while hostile-reviewer threads are unresolved (OMN-17492).


Semantics (OMN-17492 — the model finds, thread resolution gates)

Surface Meaning Blocks merge?
Review threads Per-finding, posted by the reviewer No (informational)
Hostile Review Thread Gate Deterministic: unresolved hostile-reviewer threads exist Fails until resolved (not yet a required context)
degraded verdict Fewer than 2 models succeeded (infra) Fails this job with a named reason

Powered by omniintelligence.review_pairing.cli_review — multi-model adversarial review: qwen3-review (Qwen3.8-27B), gpt-oss-review (gpt-oss-120b) (OMN-8468/OMN-8524/OMN-17492)

…tadata

The stand-in request model now declares a metadata field to accept the caller_lane payload.
This prevents extra_forbidden errors when the delegate always includes caller metadata.
The model still omits requested_timeout_seconds to preserve existing test coverage.
Local integration and unit test suites pass with this change.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hostile Reviewer — adversarial findings (OMN-17492)

Models succeeded: qwen3-review, gpt-oss-review
Models failed: none
New finding threads: 0
Deduped (already posted on this PR): 0
Below quorum (one model only, reported not threaded): 14
Nit-level findings suppressed: 0

The model is the FINDER, never the gate: merge is gated only by the
deterministic Hostile Review Thread Gate, which blocks while
hostile-reviewer threads are unresolved. Resolve each thread after
addressing (or rejecting, with a reply) its finding.

Below quorum: 14 finding(s) raised by one model only (OMN-18479)

These are reported and NOT dropped, but they get no thread and do not block: a single model's finding no other model reproduced is not evidence enough to stop a merge. Read them; act on them if they are right.

  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py (qwen3-review) — Session fallback bypasses UUID validation and slug sanitization | The session fallback path in _fallback_lane returns f"session:{session}" without applying _slug or truncating to _LANE_MAX. The sessio
  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py (qwen3-review) — Unsanitized environment variables in derived lane tokens | The GITHUB_WORKFLOW and GITHUB_REPOSITORY values are passed through _slug, which is correct. However, the XPC_SERVICE_NAME value is used dire
  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py (qwen3-review) — Truncation to _LANE_MAX may produce invalid lane tokens | The _derived function truncates the joined string to _LANE_MAX characters. If the truncation point falls in the middle of a slugified part, th
  • [MAJOR] tests/unit/cli/test_cli_delegate_caller_never_null.py (qwen3-review) — Missing test for session fallback with non-UUID session values | The test suite verifies that CLAUDE_CODE_SESSION_ID with a valid UUID produces a session lane, but does not test what happens when the
  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py:71-78 (gpt-oss-review) — Unsanitized session ID used in lane token | The fallback logic inserts the raw CLAUDE_CODE_SESSION_ID value into the lane token ("session:{session}") without applying the slug sanitization used for ot
  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py:73-78 (gpt-oss-review) — Uncaught exception from socket.gethostname() | When host is not provided, resolve_delegate_caller calls socket.gethostname() to obtain the fallback host name. socket.gethostname() can raise `O
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py (qwen3-review) — socket.gethostname() called on every resolve when host is None | When host is None, resolve_delegate_caller calls socket.gethostname() unconditionally in the fallback path. This is a system call that
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py (qwen3-review) — GITHUB_REPOSITORY rsplit produces 'unknown' for empty repository | When GITHUB_REPOSITORY is empty or unset, repository becomes an empty string, and the code uses repository or "unknown". This is corr
  • [MINOR] tests/unit/cli/test_cli_delegate_caller_never_null.py (qwen3-review) — No test for _slug edge cases with leading/trailing dots and underscores | The slug function strips leading and trailing '-', '.', and '' characters. The test suite does not verify that a value like
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py (qwen3-review) — lane_source string is not machine-parseable | The lane_source values are human-readable strings like "fallback env XPC_SERVICE_NAME" or "fallback host". These are used in the ModelDelegateCaller model
  • [MINOR] tests/unit/cli/test_cli_delegate_caller_never_null.py (qwen3-review) — No test for launchd label that exceeds _LANE_MAX | The _LANE_MAX constant is 128, and the launchd path truncates to this limit. No test verifies that a launchd label longer than 128 characters is corr
  • [MINOR] src/omnibase_infra/cli/model_delegate_caller.py:13-20 (gpt-oss-review) — ModelDelegateCaller lane field remains Optional despite non‑null guarantee | The resolver now guarantees lane is never None, but the Pydantic model still declares lane: str | None. This type mis
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py:30-78 (gpt-oss-review) — Additional regex and slug processing on every caller resolution | The new fallback logic introduces three compiled regular expressions and multiple string transformations for each call to `resolve_del
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py:56-64 (gpt-oss-review) — Missing test for overly long launchd label truncation | The fallback for launchd jobs truncates the lane token to _LANE_MAX characters, but the test suite does not verify behavior when the label exc

@jonahgabriel
jonahgabriel added this pull request to the merge queue Oct 1, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hostile Reviewer — adversarial findings (OMN-17492)

Models succeeded: qwen3-review, gpt-oss-review
Models failed: none
New finding threads: 2
Deduped (already posted on this PR): 0
Below quorum (one model only, reported not threaded): 9
Nit-level findings suppressed: 0

The model is the FINDER, never the gate: merge is gated only by the
deterministic Hostile Review Thread Gate, which blocks while
hostile-reviewer threads are unresolved. Resolve each thread after
addressing (or rejecting, with a reply) its finding.

Below quorum: 9 finding(s) raised by one model only (OMN-18479)

These are reported and NOT dropped, but they get no thread and do not block: a single model's finding no other model reproduced is not evidence enough to stop a merge. Read them; act on them if they are right.

  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py (qwen3-review) — Session fallback bypasses UUID validation and slugification | The session fallback path in _fallback_lane returns f"session:{session}" without passing the session value through _slug or validati
  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py (qwen3-review) — Launchd label fallback bypasses slugification and length bound | The launchd fallback path returns f"launchd:{label}"[:_LANE_MAX] without passing the label through _slug. The `_LAUNCHD_LABEL_PATTE
  • [MAJOR] src/omnibase_infra/cli/delegate_caller.py (gpt-oss-review) — Uncaught exception from socket.gethostname in fallback lane generation | The fallback lane logic calls socket.gethostname() when host is not supplied. If the system call fails (e.g., due to DNS mi
  • [MAJOR] src/omnibase_infra/cli/model_delegate_caller.py (gpt-oss-review) — ModelDelegateCaller lane field remains optional despite resolver guarantee | ModelDelegateCaller.lane is typed as str | None and defaults to None, yet resolve_delegate_caller now always return
  • [MAJOR] tests/unit/cli/test_cli_delegate_caller_never_null.py (gpt-oss-review) — Missing test for hostname‑fallback error handling | The new fallback path relies on socket.gethostname(), but the test suite does not simulate a failure of this call. Absence of such a test leaves t
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py (qwen3-review) — Host name is attacker-controllable via socket.gethostname() | When host is not explicitly provided, socket.gethostname() is used to construct the unattributed:<host> lane. On multi-tenant or con
  • [MINOR] tests/unit/cli/test_cli_delegate_caller_never_null.py (qwen3-review) — Test for host fallback does not validate lane token format | The test test_caller_lane_host_fallback_is_always_a_lane_token parametrizes host values including "", " ", "host with spaces!", `
  • [MINOR] tests/unit/cli/test_cli_delegate_caller_never_null.py (qwen3-review) — No test for the case where GITHUB_ACTIONS is true but GITHUB_WORKFLOW is empty | The GHA fallback requires both GITHUB_ACTIONS to be "true" and GITHUB_WORKFLOW to be non-empty. If `GITHUB_ACTION
  • [MINOR] src/omnibase_infra/cli/delegate_caller.py (gpt-oss-review) — Non‑deterministic lane token due to host‑dependent fallback | When no explicit lane is provided, the resolver derives a lane from the machine hostname. Hostnames differ across environments, causing th

Findings demoted from threads (anchor rejected)

  • [MINOR] hostile-reviewer (qwen3-review)

    GitHub Actions repository extraction uses rsplit which may produce unexpected results | The GHA fallback extracts the repository name via environ.get("GITHUB_REPOSITORY", "").strip().rsplit("/", 1)[-1]. If GITHUB_REPOSITORY is set to a value without a slash (e.g., just "omnibase_infra"), the result is the entire string, which is correct. However, if the value is empty or contains multiple slashes, the behavior is correct but the code does not explicitly handle the case where GITHUB_REPOSITORY is uns

    Resolve this thread when addressed — the Hostile Review Thread Gate blocks while hostile-reviewer threads are unresolved (OMN-17492).

  • [MINOR] hostile-reviewer (gpt-oss-review)

    Potential lane token collision from length truncation | The fallback lane strings are truncated to _LANE_MAX characters after concatenation. Truncation can cut off part of the identifier, producing identical tokens for distinct inputs (e.g., long hostnames or GitHub repo names), which may lead to misattribution in the ledger. | Evidence: Both _derived and the launchd fallback use [:_LANE_MAX] on the full token string. | Fix: Apply truncation to each component before joining, or hash the excess portion

    Resolve this thread when addressed — the Hostile Review Thread Gate blocks while hostile-reviewer threads are unresolved (OMN-17492).

Merged via the queue into dev with commit b03fdac Oct 1, 2026
167 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-20299-caller-lane-never-null branch October 1, 2026 14:24
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