Repository navigation
feat(OMN-13407): expose delegation context pack hash - #1335
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (5)
📝 WalkthroughWalkthroughAdds ChangesContext Pack Hash — Full Delegation Pipeline
Dependency Management Updates
Sequence DiagramsequenceDiagram
participant Client as Delegation Client
participant Orchestrator as handler_delegation_workflow
participant LLM as apply_inference_protocol
participant EventBus as ModelTaskDelegatedEvent
participant Projector as HandlerProjectionDelegation
participant DB as delegation_events
Client->>Orchestrator: ModelDelegationRequest(context_pack, context_pack_hash)
Orchestrator->>Orchestrator: _prompt_with_context_pack(context_pack, prompt)
Orchestrator->>LLM: context-prefixed prompt
LLM-->>Orchestrator: inference result
Orchestrator->>EventBus: emit ModelTaskDelegatedEvent(context_pack_hash)
EventBus->>Projector: project(event) / project_delegate_skill_terminal(row)
Projector->>DB: UPSERT delegation_events SET context_pack_hash
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
| 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 |
ARCH-DI-002: Direct Handler instantiation (warning)\n\n\nsrc/omnimarket/nodes/node_delegation_orchestrator/handlers/handler_delegation_workflow.py:365: loop = HandlerComplianceLoop()\n\n\nHandler classes should be wired only in workflow_runner.py or adapter modules. Direct instantiation elsewhere bypasses the contract system.\n\n
Powered by node_pr_review_bot
✅ PR Review Bot — CLEANFindings: 0
Verdict semantics
Powered by node_pr_review_bot — Qwen3-Coder-30B reviewer + DeepSeek-R1 judge (OMN-7963) |
|
| Verdict | Meaning | Blocks merge? |
|---|---|---|
passed |
No critical/error findings | No |
blocked |
CRITICAL or ERROR findings found | Yes |
degraded |
All models unavailable (infra) | No (pilot) |
Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524)
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/omnimarket/models/delegation/wire/model_delegate_skill_terminal_projection.py`:
- Around line 71-74: The context_pack_hash field in the
ModelDelegateSkillTerminalProjection class does not normalize whitespace during
ingest, allowing hashes with different surrounding whitespace to be treated as
distinct values. Add a field validator to the context_pack_hash field that
strips and normalizes whitespace from the input value before it is stored. This
ensures that hashes differing only by whitespace are treated as equivalent for
readback and grouping operations.
In `@src/omnimarket/models/delegation/wire/model_delegation_request.py`:
- Around line 102-115: The `context_pack` and `context_pack_hash` fields in the
ModelDelegationRequest class can become inconsistent when the
`_prompt_with_context_pack` method strips or skips injection of context_pack for
whitespace-only or empty values, but still propagates the hash downstream. Add
validation logic to ensure that whenever context_pack is determined to be empty
or whitespace-only and will not be injected, the context_pack_hash field is also
cleared to an empty string. This maintains the invariant that a non-empty
context_pack_hash only exists when actual context has been injected, preventing
OFF-arm runs from being incorrectly labeled as context-injected in analytics.
In
`@src/omnimarket/nodes/node_delegation_orchestrator/handlers/handler_delegation_workflow.py`:
- Around line 328-334: The `_prompt_with_context_pack` function calls `.strip()`
on `request.context_pack` without first checking if it is None, which will raise
an AttributeError when the field is absent. Add a None check before calling
`.strip()` on `request.context_pack` in line 329. The fix should check if
`request.context_pack` is None or empty and return the original prompt unchanged
in those cases, similar to how the current code handles empty strings. This will
prevent crashes on the initial inference and compliance-repair workflow paths.
In
`@src/omnimarket/nodes/node_projection_delegation/handlers/handler_delegation.py`:
- Line 840: The UPSERT conflict resolution for context_pack_hash unconditionally
overwrites the existing value with EXCLUDED.context_pack_hash, which can replace
a valid hash with an empty string when the incoming payload omits this field.
Modify the context_pack_hash assignment in the UPSERT clause to use conditional
logic that only updates the hash when the incoming EXCLUDED.context_pack_hash
value is non-empty, otherwise preserve the existing context_pack_hash value from
the target table.
In
`@src/omnimarket/nodes/node_projection_delegation/handlers/handler_projection_delegation.py`:
- Line 342: The `context_pack_hash` field at line 342 is not being protected by
the `_preserve_existing_evidence` function, allowing sparse compat events with
empty `context_pack_hash` to overwrite existing non-empty values from prior
terminal events. Add `context_pack_hash` to the preservation logic in
`_preserve_existing_evidence` using the `_is_blank()` method to check if the
field is blank, following the same pattern already used for `prompt_text` and
`response_text` fields in the preservation loops. Additionally, update the test
`test_sparse_task_delegated_event_does_not_clear_terminal_evidence` to include
an assertion that verifies `context_pack_hash` is properly retained after sparse
updates.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9cd2e359-8691-476e-a0a7-7e570330a3f0
📒 Files selected for processing (11)
src/omnimarket/models/delegation/wire/model_delegate_skill_terminal_projection.pysrc/omnimarket/models/delegation/wire/model_delegation_request.pysrc/omnimarket/models/delegation/wire/model_task_delegated_event.pysrc/omnimarket/nodes/node_delegation_orchestrator/handlers/handler_delegation_workflow.pysrc/omnimarket/nodes/node_projection_delegation/contract.yamlsrc/omnimarket/nodes/node_projection_delegation/handlers/handler_delegation.pysrc/omnimarket/nodes/node_projection_delegation/handlers/handler_projection_delegation.pysrc/omnimarket/nodes/node_projection_delegation/migrations/0020_delegation_context_pack_hash.sqltests/test_golden_chain_projection_delegation.pytests/test_projection_delegation_api_contract.pytests/unit/delegation/test_delegation_chain_e2e.py
* fix(OMN-13409): summarization quality gate rejects heuristic refusal (#1334)
Two-layer fix for the defect where LLM refusals were recorded as
delegation-completed with quality_passed=True.
Root cause: (1) _check_no_refusal only matched 5 specific phrases,
missing soft refusals like "No.", "This request cannot be fulfilled.";
(2) handler_delegation_workflow.handle_gate_result recomputed acceptance
from fail_category + score alone, ignoring result.passed, so any gate
result with score >= required_bar (0.8) was accepted regardless of
fail_category=fail_heuristic.
Fix:
- handler_quality_gate: extend _REFUSAL_PHRASES to cover 18 additional
common soft-refusal patterns; add ultra-short content-free refusal
pre-pass (<=5 words, all tokens in _NEGATION_TOKENS) that catches
"NO", "No.", "N/A" and similar token-count-bounded declinations
- handler_delegation_workflow: add result.passed to quality_accepted so
the orchestrator honours the gate's authoritative verdict; a gate result
with passed=False never emits delegation-completed
- Update test_delegation_tier_escalation: correct pre-OMN-13409 test that
pinned the broken behaviour (passed=False + high score => COMPLETED)
- Add test_refusal_not_pass_omn13409: failing test (red) then green after
fix; drives the real dispatch path (routing->inference->gate->orchestrator)
with dogfood repro inputs (cid 758871f8: "NO" 2-token response)
Evidence: dogfood cid 758871f8, 10 new tests (all red before, all green
after), 2379 unit tests pass, calibration ratchet passes.
* feat(OMN-13407): expose delegation context pack hash (#1335)
* feat(OMN-13407): expose delegation context pack hash
* chore(OMN-13407): pin omnibase core context hash DTOs
* fix(OMN-13407): normalize context pack hash projection
* chore(OMN-13407): pin merged core context DTOs
* fix(OMN-13408): populate served tokens_input/tokens_output on delegation compat events (failed + completed paths) so the projection upsert is not clobbered to 0; bump omnibase-core rev; add regression test (#1336)
* fix(OMN-13408): capture served cloud usage on the inference-effect truncation raise so canonical delegation terminals record real tokens + cost (#1337)
PRIMARY drop: the cloud inference EFFECT discarded the provider's served usage
when the response truncated. HandlerInferenceIntent._call_llm raised
finish_reason=length (and empty-content / empty-choices) BEFORE parsing the
usage block, and the error-path ModelInferenceResponseData omitted token fields
-> defaulted to 0/0/0. The merged carry-fix (tokens_input/output on the compat
event + cumulative accumulator in _escalation_metadata) is correct but read from
that zero source, so the canonical delegation-failed.v1 terminal still recorded
0 tokens / $0 cost on the escalated-and-failed path (live CID badf851a).
Fix (market-only, no core change -- the token fields already exist on the wire
DTOs at core 81c865f6):
- handler_inference_intent.py: parse usage BEFORE the truncation/empty raises and
raise a typed InferenceUsageError carrying prompt/completion/total tokens; the
error-path response in handle() threads those onto ModelInferenceResponseData.
Transport failures (raised before response.json()) carry no usage -> 0 fallback.
- handler_delegation_workflow.py: the inference-error terminal-FAILED path built
ModelDelegationResult with total_tokens=response.total_tokens; a reasoning model
reports total > prompt+completion (thinking tokens), so the wire invariant
total==prompt+completion raised ValidationError and crashed the dispatcher with
NO terminal emitted. Derive total from prompt+completion (matches the gate-path
reconciliation in _record_inference_response). Surfaced once real served tokens
flow (before, 0==0+0 passed by accident).
Tests: new test_served_usage_upstream_omn13408.py asserts the EFFECT carries
served usage on truncation, the typed exception carries usage, and the CANONICAL
delegation-failed.v1 ModelDelegationResult (not just the compat twin) carries
non-zero served tokens + priced cost on a metered escalated-failed scenario.
Updated existing inference-effect tests to assert served tokens on the
truncation/empty paths and zero-fallback on transport failure.
dod_evidence: targeted+full pytest (7917 passed; 3 failures are pre-existing
@pytest.mark.kafka broker-dependent integration tests, unrelated), ruff + mypy
--strict clean, pre-commit all-files green.
* feat(OMN-13470): combine LLM-judge adequacy score into the delegation quality gate (#1338)
* feat(OMN-13470): combine LLM-judge adequacy score into delegation quality gate
The deterministic check set for verifiable code classes (compiles_without_errors
/ final_artifact_only / passes_existing_tests) is a HARD FLOOR for refusals and
empties but is too strict to be the sole adequacy authority: a correct but
mechanically-incomplete cloud code_generation answer scores ~0.733 deterministic
and fails the 0.85 required_bar, so no cloud row passes the gate today
(delegation_judge_verdict_events = 0; HWM = 0).
This wires the judge that OMN-13367 declared but never produced:
- HandlerJudgeAdequacy (drop-in judge handler) builds a rubric-grounded adequacy
prompt over (task, candidate, acceptance criteria) and runs it through the
canonical inference bridge (AdapterInferenceBridge) -- an EFFECT, never an
inline model call inside the pure gate reducer. It parses a 0-1 adequacy score
and builds a durable, replayable ModelDelegationJudgeVerdictEvent. Fail-closed
to JUDGE_FAILED (actual_score=None) on call/parse failure -- no silent zero.
- delta() gains judge_adequacy_score: when the deterministic floor passes on a
verifiable path it COMBINES the deterministic band fraction with the judge
score (0.6/0.4) and records score_source='combined'. A refusal/empty hard-
blocks in the deterministic branch before the combine, preserving the
OMN-13409 refusal-not-pass invariant.
- HandlerQualityGateIntent.handle_async runs the judge effect for
code_generation/test, threads the score into delta(), and emits BOTH the gate
result and the judge verdict event (delegation-judge-verdict.v1) for the
existing projection.
- delegation_judge_rubrics.v1.yaml gains delegation_code_adequacy_v1 for the
verifiable code classes.
Real-dispatch-path tests: a good-but-mechanically-incomplete code answer + a
high judge score completes via the combined score; a refusal does NOT complete
even with a maximal judge score. Judge verdict captured + replayable
(event_hash recomputes deterministically).
* fix(OMN-13470): separate judge model and node versions
* test(OMN-13157): next_eligible_tier honors task-class tier_order (closed-set regression tests) (#1339)
* test(OMN-13157): add closed-set tier_order regression tests for next_eligible_tier
Adds TestClosedSetTierOrderOMN13157 to test_delegation_tier_escalation.py,
directly asserting the three acceptance criteria for the closed-set fix:
1. After cheap_cloud fails, code_generation escalates to local (contract order
[cheap_cloud, local, claude] honored even though local precedes cheap_cloud
in routing_tiers.yaml declaration order).
2. task_type=None calls remain forward-only in routing_tiers.yaml declaration
order (backward compat preserved).
3. cheap_frontier is excluded from code_generation by the closed-set contract
(tier absent from tier_order is NEVER tried, without needing it in
excluded_tiers).
The handler fix (removing the append-unlisted code from _tier_order_from_contract)
landed in dev via prior commits; these tests serve as the regression guard so
the bug cannot reopen undetected.
Refs OMN-13157
* test(OMN-13157): pin bifrost fixture so closed-set exclusion test is env-deterministic
CI 'Tests (Split 1/1)' failed on test_cheap_frontier_excluded_from_code_generation_by_closed_set
with 'assert None == local': the test did not pin a bifrost config, so the local tier backend
was unresolvable in CI and next_eligible_tier(task_type=code_generation) skipped it (returning
None instead of local). Add the frontier_unconfigured_bifrost fixture (resolvable local +
cheap_cloud, empty claude/frontier) to make the closed-set assertion deterministic across
environments. The task_type=None declaration-order branch never consults bifrost, so the
backward-compat baseline is unaffected.
Refs OMN-13157
* fix(OMN-13467): commit input offset only after terminal broker-ACK; terminal publish failure propagates (no at-most-once gap) (#1340)
node_generation_consumer previously committed the input Kafka offset BEFORE
the terminal event (node-generation-completed/failed) was durably published. A
restart between commit and publish dropped the terminal event for a fresh
subscriber (at-most-once gap, G3 dogfood cid dg-20260621T233602Z-G3-original).
Fix:
- _emit_benchmark / _emit_deploy / _emit_registration / _try_tool_reuse_short_circuit
are now async; handle() awaits each so the terminal event is broker-ACKed
BEFORE handle() returns. The at-least-once wiring layer commits the input
offset only after handle() returns without raising, giving the ordering:
publish terminal -> await broker-ack -> handle() returns -> commit offset.
- The terminal-event publish failure now PROPAGATES out of _emit_benchmark
(no swallowing try/except). A broker that does not ACK the terminal raises,
handle() raises, the offset is NOT committed, and the input is re-delivered.
The replay guard makes re-processing idempotent and re-emits the terminal.
This closes the silent-offset-advance window (DoD item 2).
- EventPublisher accepts sync OR async callables; _await_publish awaits
coroutines and is a no-op for sync publishers (test back-compat).
Tests (tests/unit/.../test_offset_commit_after_terminal_ack_omn13467.py):
- restart path: a crash between publish and commit re-delivers the input and a
fresh subscriber still sees exactly one terminal event (no loss).
- terminal publish failure propagates out of handle() (offset not committed).
- terminal publish failure does NOT write the replay marker (re-delivery
re-runs and re-emits).
- async publisher is awaited before handle() returns; sync publishers still work.
* fix(OMN-12460): normalize raw dict/StartCommand payload in node_plan_to_tickets handler (#1344)
The RuntimeLocal dispatch boundary delivers the contract input_model
(ModelPlanToTicketsStartCommand) as a raw dict for operation_match handlers,
or as a ModelPlanToTicketsStartCommand instance on the single-handler path —
not the ModelPlanToTicketsRequest the handler operated on. handle() accessed
request.plan_content directly, raising AttributeError and producing the live
30s onex run-node timeout.
Fix: HandlerPlanToTickets.handle now accepts
ModelPlanToTicketsRequest | ModelPlanToTicketsStartCommand | dict and
normalizes the payload (reading plan_path into plan_content for the command
shapes) before any attribute access. Adds unit tests covering the raw-dict,
StartCommand-instance, and model_validate(dict) deserialization paths plus an
AttributeError regression guard.
dod_evidence: full suite 7922 passed/64 skipped (not kafka); CLI + both
dispatch-boundary payload shapes (dict + StartCommand) parse to status=parsed;
mypy --strict clean; pre-commit --files clean.
Evidence-Source: HEAD
* feat(OMN-13151): Phase 0 registry unification — extend ModelContentDiscoveredEvent + duty-critical capture-topic metadata (#1343)
* docs(OMN-13102): live materialization proof for pattern_learning golden chain on stability-test lane (#1341)
A real onex.evt.omniintelligence.pattern-stored.v1 event published to the
proof lane (stability-test) flows end-to-end through the live
projection_pattern_learning consumer and UPSERTs a row in
omnidash_analytics.pattern_learning_artifacts with correlation_id.
Decision-tree reconciliation: consumer + migration + golden_chains.yaml entry
+ infra-vendored migration (with correlation_id reconcile ALTER) all already
merged via OMN-13124 (#1210). This packet supplies the live proof OMN-13102's
DoD requires.
Evidence: docs/evidence/2026-06-22-OMN-13102-pattern-learning-golden-chain-live-proof.md
* test(OMN-13473): reconcile delegation orchestrator imperative FSM against contract.yaml (#1342)
Audit node_delegation_orchestrator's imperative _transition FSM (15 call
sites, _VALID_TRANSITIONS guard table) against the declared contract.yaml
fsm.transitions table. Three handler-driven ROUTED-origin edges were
undeclared in the contract:
ROUTED -> ESCALATING (retryable inference infra-error escalation, OMN-13140)
ROUTED -> FAILED (terminal inference failure / pre-EXECUTING lifecycle FAILED)
ROUTED -> COMPLETED (pre-EXECUTING lifecycle COMPLETED)
All three are real, reachable transitions, so the contract (single source of
truth) is updated to declare them rather than removing them from the handler.
Adds tests/unit/delegation/test_delegation_fsm_reconciliation.py with a
documented 1:1 mapping of every _transition call site to its contract entry
(HANDLER_TRANSITION_CALLSITES) plus assertions that:
- handler _VALID_TRANSITIONS is a subset of contract fsm.transitions
- every documented call-site edge is declared in the contract
- every documented edge is permitted by the handler guard table
- the mapping covers every _VALID_TRANSITIONS edge (no undocumented edges)
- no contract edge is unreachable from the handler (no dead transitions)
No behavior change. 23 new tests, full delegation suite (332) green, full
non-kafka suite (7941 passed) green, mypy --strict clean, pre-commit green.
* fix(OMN-13475): single contract-owned terminal+compat builder kills OMN-13408 telemetry-zeroing class (#1346)
Collapse the five ad-hoc delegation terminal-emission sites onto ONE builder
(HandlerDelegationWorkflow._emit_terminal) fed by a single TerminalEmissionInputs
value object. Cost is measured exactly once and the served tokens are read once;
both the canonical terminal (ModelDelegationResult on delegation-completed/failed.v1)
and the compat twin (ModelTaskDelegatedEvent on task-delegated.v1) are derived from
those identical values. The two events co-write the same delegation projection row,
so structural single-sourcing makes the OMN-13408 token/cost-zeroing divergence
impossible rather than patched per-site.
- New _emit_terminal is the sole ModelDelegationResult / ModelTaskDelegatedEvent
construction site (was 5 + 3 independent constructions).
- _build_compat_event -> _gate_terminal_inputs (returns inputs, not an event).
- Deleted orphaned _escalation_metadata (folded into the single builder).
- Does not regress the inference EFFECT served-usage fix (OMN-13408, merged).
dod_evidence:
- tests/unit/delegation/test_terminal_builder_single_source_omn13475.py:
structural (exactly-one-construction) + parity (token/cost) across completed,
failed-inference, and agent-lifecycle terminal paths.
- OMN-13408 fixtures stay green: test_compat_event_tokens_not_clobbered_omn13408,
test_cumulative_cost_accumulation_omn13408, test_terminal_path_cost_usd_omn13396.
- Full local gates: ruff format+check clean; mypy --strict clean (2461 files);
pytest tests/ -> 7969 passed, 68 skipped, 3 failed (all @pytest.mark.kafka
live-broker integration tests requiring :19092, env-only, unrelated).
Evidence-Source: PENDING_PUSH
Evidence-Ticket: OMN-13475
* fix(OMN-13470): judge resolves concrete model + terminal fail-closed — dogfooded on real architecture (no mocks) (#1347)
* fix(OMN-13470): judge resolves concrete model + terminal fail-closed — dogfooded on real architecture (no mocks)
- Judge resolves a CONCRETE model (glm-5.2) + COMPLETE verbatim endpoint +
secret_ref from the routing authority (resolve_delegation_backend, backend_id
cloud-glm) via new RoutingResolvedJudgeInferenceAdapter — never passes a
delegation TIER name to the inference layer. Fixes the live
'ValueError: Unknown model_key: cheap_cloud' that made every judge verdict
judge_failed.
- Terminal path fails CLOSED: _terminal_failed_fields coerces None
model_used/endpoint_url/content so a valid delegation-failed terminal is
ALWAYS emitted (no ValidationError, no dropped terminal). Also fixes
OMN-13140 all-tiers-failed-terminal-must-emit.
- ModelResolvedDelegationBackend gains secret_ref; resolve_delegation_backend
gains backend_id targeting.
- Rubric judge_model_version label cheap_cloud -> cloud-glm (replay identity is
rubric_hash, unchanged).
- Rewrote tests/unit/delegation/test_quality_gate_judge_combine_omn13470.py:
DELETED _FakeBridge + patch(httpx.Client) + the
judge_model_version=='routing-contract:cheap_cloud' assertion. Added
routing-authority concrete-resolution asserts, a RECORDED-FROM-REAL-GLM replay
adapter that hard-rejects a tier name, a live cloud GLM test
(OMN_ALLOW_LIVE_JUDGE_CALL gate), and a REAL EventBusInmemory chain proving
the refusal terminal still fails closed to a valid delegation-failed event.
Dogfooded: real z.ai GLM cloud call returns verdict=pass actual_score=1.0 on the
production default path.
Evidence: docs/evidence/2026-06-22-omn13470-judge-dogfood/
* test(OMN-13470): real-bus chain resolves routing from self-contained bifrost contract (host-independent for CI)
* fix(OMN-13498): B1 — replace 4 golden-chain fake inference adapters with recorded-from-real replay (no boundary fakes) (#1349)
* feat(OMN-13497): no-faked-boundary acceptance corpus + SEA generation driver (#1348)
Adds the corpus_no_faked_boundary acceptance corpus (8 violation incl 4
adversarial mutations + 7 clean fixtures) that gates a generated COMPUTE
validator banning hand-written fakes/stubs of the platform's own inference /
routing / dispatch boundary, and registers no-faked-boundary in the CORPORA
registry + drive_validator_generation task descriptions.
Generated via the REAL SEA path against the live local model Qwen3.6-35B-A3B
(local-coder backend, routing resolved from contract); corpus-accepted attempt
4 (corpus_passed=true, all 8 violations flagged, all 7 clean passed). The
c-mut-mock-nonboundary-base clean fixture pins the one true FP found in the
shadow run (a Mock class subclassing a service-registry mixin is a test
harness, not a fake of the inference boundary).
OMN-13497
* build(OMN-13504): re-lock omnimarket dev omnibase-core pin to merged dev SHA 81c865f6 (#1350)
Repoint omnibase-core git pin from 79ec3ad9d801b8da38b4d9099852cc8bfffad00e
(unmerged OMN-13408 feature branch SHA, NOT an ancestor of core dev) to
81c865f69a15781d526125ee13f39834a3ce69fd (squash-merge of #1300 / OMN-13408
on omnibase_core dev, byte-identical content -- git diff between the two is
empty). The prior pin made the OMN-12977 sibling-pin guard abort the dev
runtime workspace build because 79ec3ad9 is not an ancestor of core dev.
Only pyproject.toml [tool.uv.sources] rev= + [tool.uv] override-dependencies
git URL and uv.lock change. No source, version specifier, migration, schema,
endpoint, or env change.
Evidence-Source: HEAD
* feat(OMN-13476): extract delegation escalation/tier decision into stateless COMPUTE node (#1352)
Track A W4 of the delegation-orchestrator decomposition (epic OMN-13471). The
escalate-or-terminate decision (whether to escalate and to which tier) was
inlined twice in HandlerDelegationWorkflow (inference-error + quality-gate-fail
branches), both applying the same precedence. Extracts that single decision
into node_delegation_escalation_decision_compute, a NodeCompute archetype
(contract + HandlerEscalationDecision: pure handle(request)->result, stateless,
deterministic, zero I/O).
The orchestrator resolves the config-dependent inputs (next eligible tier +
precise no-higher-tier reason, which read routing_tiers.yaml + the task-class
contract) and DELEGATES the verdict via _decide_escalation. Behavior preserved:
retryability -> budget -> current-tier -> ladder precedence, OMN-13167 precise
no-higher-tier reason, OMN-13140 escalation-proof emission all unchanged. Tiers/
providers/models resolved from the routing contract+overlay, never env. Shared
decision request/result models live in omnimarket.routing so both nodes import
them without a cross-node reach-in.
Behavior proof: 15 TDD-first COMPUTE unit tests + the full 370-test delegation
unit suite green unchanged.
Evidence-Source: contracts/OMN-13476.yaml (onex_change_control)
Evidence-Ticket: OMN-13476
* fix(OMN-13499): migrate reference delegation golden chain to canonical replay harness (#1351)
Reference-chain migration for Phase 0 (epic OMN-13498). Replaces the
patch("httpx.Client") boundary-fake in the delegation useful-artifact golden
chain with the canonical omnibase_core RecordedReplayInferenceTransport.
The chain now runs FOR REAL on replay: HandlerDelegationWorkflow routing,
HandlerRoutingIntent contract resolution + backend selection,
handle_routing_decision endpoint/model resolution, HandlerInferenceIntent's real
OpenAI-compatible request construction (system prompt + inference-protocol
shaping + max_tokens/temperature/chat_template_kwargs) + POST, and the quality
gate. Only the model response bytes come from a provenance-stamped fixture
recorded from the real request the live path constructs (concrete model
Qwen3.6-35B-A3B, request_hash-pinned).
Adds the routing-failure proof: a chain resolving the WRONG model FAILS the
replay (REQUEST_HASH_MISMATCH), and a delegation tier name as the model FAILS
(ROUTE_NOT_RESOLVED) — replay is evidence, not authority. check-no-faked-boundary
is clean on the migrated file (blocking mode, exit 0).
Bumps the dev core git pin to the OMN-13499 harness commit so omnimarket imports
the canonical harness from core (no layering violation).
OMN-13499
* refactor(OMN-12805): resolve provider URLs via routing authority, fail-closed (#1345)
* refactor(OMN-12805): resolve provider URLs via routing authority, fail-closed (no hardcoded defaults)
Replace hardcoded provider base-URL literals + silent env defaults at the
named call sites with routing-authority resolution that fails closed when the
URL is unconfigured (epic OMN-12803):
- node_swarm_fleet_discovery_effect: drop _OPENROUTER_BASE_URL literal; resolve
the OpenRouter base URL from OPENROUTER_BASE_URL routing config, raising
ValueError when unset (no baked-in provider URL).
- node_build_loop_orchestrator/assemble_live: resolve the local coder
endpoint+model through ModelPolicyLoader (model_policy.yaml 'coder' policy)
instead of raw LLM_CODER_URL / LLM_CODER_MODEL_NAME env reads; add
resolve_model_id_optional to preserve tiered-fallback skip semantics.
- config/settings: drop the hardcoded example provider URL from the GLM field
description; value comes from the routing authority only.
Adds a golden-chain test for node_swarm_fleet_discovery_effect (the
coverage gate fires because the node is changed) covering the config-resolved
base URL and the fail-closed path, plus a fail-closed unit test in the
existing handler test module.
* chore(OMN-12805): sync generated env example
* build(OMN-13499): relock omnimarket dev core pin to merged dev SHA (8ecb7efc1) (#1354)
* build(OMN-13499): relock omnimarket dev core pin to merged dev SHA 8ecb7efc1
The OMN-13499 product PR (#1351) merged pinning omnibase-core to 4c91bc18, a
golden-chain-harness fix commit that was superseded on the core branch (replaced
by fb9e2da13, then squash-merged to core dev as 8ecb7efc1 via #1306). 4c91bc18 is
NOT an ancestor of omnibase_core origin/dev, so the OMN-12977 sibling-pin guard
would abort the dev runtime workspace build.
This repoints [tool.uv.sources] rev= and the [tool.uv] override-dependencies git
URL (mirrored into uv.lock via uv lock --no-upgrade) to the merged dev SHA
8ecb7efc17721dda2ce468b2e5051816ff8e89bc, making the pin an ancestor of core dev.
Content-equivalent (the harness bytes are identical); same version 0.46.0; no
source/specifier/migration/schema/endpoint/env change.
Verified the canonical golden-chain harness imports cleanly from omnimarket at
this pin (from omnibase_core.runtime.golden_chain import
RecordedReplayInferenceTransport, load_fixture, record_fixture), unblocking
GC B2 (OMN-13500) / B3 (OMN-13501) migration.
* chore(OMN-13499): refresh relock checks
* build(OMN-13505): re-pin omnibase_core dev source to 1b22fd18 (typed workflow_definition field) (#1353)
* build(OMN-13505): re-pin omnibase_core dev source to 1b22fd18 (OMN-12835 typed field)
Re-pin omnimarket's omnibase_core dev git source from 81c865f6 (pre-OMN-12835)
to current core origin/dev HEAD 1b22fd18bc0c3aebe9501da63dc57e33a47a3d70, which
carries OMN-12835 (commit 3048cecab): the typed contract-side execution-graph
DAG wired into orchestrator contracts.
- pyproject.toml [tool.uv.sources] rev -> 1b22fd18
- pyproject.toml git+@ dependency line -> 1b22fd18
- uv.lock regenerated (omnibase-core 0.46.0 81c865f6 -> 1b22fd18)
Version constraint omnibase_core>=0.45.0,<0.47.0 unchanged (core dev is v0.46.0).
core-only re-pin; omnibase_infra/compat pins untouched.
Evidence:
- core dev HEAD: 1b22fd18bc0c3aebe9501da63dc57e33a47a3d70 (v0.46.0)
- field proof: ModelWorkflowConfig.model_fields contains 'workflow_definition';
model_contract_workflow_definition.py present in installed package
- full suite: 7992 passed, 66 skipped, 9 deselected (kafka live-broker), 0 failures
- mypy src/ --strict: Success, 2463 files
- ruff format + check: clean
- pre-commit run --all-files: all gates passed
* build(OMN-13505): re-pin omnibase_core dev source to 8ecb7efc1 (converged dev HEAD)
Re-pins omnibase_core from 1b22fd18 to 8ecb7efc1 — the converged omnibase_core
dev HEAD that carries BOTH the OMN-13408 ModelTaskDelegatedEvent token/context
fields AND the OMN-12835 typed workflow_definition field. 1b22fd18 is an
ancestor of 8ecb7efc1, so this is a forward pin. Merged origin/dev to clear
BEHIND; uv.lock regenerated to match (omnibase-core v0.46.0 8ecb7efc).
This ensures that whichever order the merge-sweep lands #1353 and #1354,
omnimarket dev converges on 8ecb7efc1.
* fix(OMN-13513): declare handler_routing.operation on node_shim_scanner contract (#1355)
The node_shim_scanner COMPUTE contract declared a handler_routing block
with no routing_strategy and no operation. RuntimeLocal._validate_routing
defaults absent routing_strategy to the operation_match branch, which
requires an operation field per handler — so the contract failed closed at
startup with 'handlers[0].operation is missing', making the shim_audit
skill (uv run onex skill shim_audit) 100% non-functional.
Add routing_strategy: operation_match + operation: scan, matching every
other single-handler COMPUTE node in omnimarket. The operation value is not
runtime-matched for single-handler compute nodes (RuntimeLocal resolves the
handler from the top-level handler: block); it only needs to be non-empty to
pass _validate_routing.
Proof: with the patch, the skill loads the routing graph, instantiates
HandlerShimScanner, runs the AST scan, publishes the terminal event, and
exits 0 with a typed ModelShimScanResult (result=completed). Broken since
introduction in OMN-4419 / PR #945.
* refactor(OMN-13474): bind delegation FSM guard to contract-derived typed ModelFSMSubcontract (#1356)
W2 of the OMN-13471 imperative-orchestrator decomposition. The delegation
orchestrator handler's FSM transition guard was driven by a hardcoded
_VALID_TRANSITIONS Python dict that paralleled the contract.yaml fsm.transitions
block (reconciled to be the single source of truth in W1/OMN-13473).
This wave removes that parallel imperative FSM table. The guard table is now a
projection of a typed, executor-bound ModelFSMSubcontract (the OMN-12835 typed
contract-side workflow surface, now available on dev) built once at import from
the contract fsm block. The declared contract table is therefore the runtime
execution authority; the imperative _transition guard validates against it.
- _load_fsm_subcontract(): parses contract fsm (states/initial/terminal/
transitions) into a typed ModelFSMSubcontract; structurally validated at import
(initial/terminal membership, transition-state membership, structural
uniqueness, no-outgoing-from-terminal); fails fast on any unknown state.
- _build_valid_transitions(): projects the typed FSM edges into the
from_state -> {to_state} guard table the _transition method consumes.
- _VALID_TRANSITIONS is now that projection, not a hand-maintained literal.
Behavior preserved: the contract-derived table is byte-identical to the prior
hardcoded table (proven in tests). The per-call-site _transition(...) invocations
remain — reducing them to zero is the explicit scope of W5 (OMN-13477), which
this ticket blocks.
Tests (tests/unit/delegation/test_delegation_fsm_contract_bound_omn13474.py):
- typed FSM matches the contract verbatim;
- guard table == declared contract edges (and is a re-loadable projection);
- every declared edge is drivable by the canonical core FSM executor
(omnibase_core.utils.util_fsm_executor.execute_transition) to the same target
state the imperative guard reaches — proving the typed field is executor-bound,
not a dead schema field.
dod_evidence: 363 passed/1 skipped (tests/unit/delegation + no-cross-node);
mypy --strict clean on handler; ruff format+check clean; pre-commit all-green;
metadata-dep + runtime-sweep (322/322) PASS. Code-gen dogfooded via onex skill
delegate (Qwen3.6-35B-A3B on .201, quality gate passed) then hand-hardened for
fail-fast and typed-FSM binding.
* refactor(OMN-13477): drive delegation FSM via contract typed-FSM advance + per-step dispatch (W5) (#1357)
W5 of the OMN-13471 imperative-orchestrator decomposition. Removes the
imperative _transition guard (and its parallel _VALID_TRANSITIONS set) and the
hand-maintained isinstance ladder in handle().
- State advancement now flows through a single _advance(workflow, target)
surface that resolves each edge from the typed, executor-bound
_DECLARED_TRANSITIONS projection of the contract FSM (the OMN-13474 W2
binding) — the contract is the transition authority. Imperative _transition
call count is now 0 (asserted by an AST gate test).
- Payload dispatch is table-driven via _PER_STEP_DISPATCH (one entry per
handler_routing event_model the contract declares), with no catch-all branch;
an undeclared payload type fails closed.
Behavior preserved: full delegation unit suite (370 passed, 1 skipped) and the
async handle() dispatch path reach identical FSM state + identical terminal
events vs the per-step methods.
Evidence-Source: OCC#OMN-13477
Evidence-Ticket: OMN-13477
* fix(OMN-13514): compliance_sweep skill false-clean — share repo resolution into handler so no-arg dispatch scans real handlers (#1359)
* fix(OMN-13514): compliance_sweep skill no longer false-clean — share repo resolution into handler
The `onex skill compliance_sweep` (RuntimeLocal dispatch) path scanned 0
handlers and returned status=compliant — a trust-defeating false-clean.
Root cause (3 coupled defects):
- ComplianceSweepRequest had only `target_dirs` while the skill mapping +
contract supply `repos`; with extra=forbid a `repos` payload was rejected
and a no-arg payload defaulted target_dirs to [] -> 0 handlers.
- _DEFAULT_REPOS and repo-name->abs-path resolution lived only in __main__.py,
which the dispatch path never executes.
Fix:
- Add `repos: list[str]` to ComplianceSweepRequest (aligns contract / skill
mapping / model).
- Move _DEFAULT_REPOS + resolve_target_dirs() into the handler module; both
__main__ and the dispatch path now resolve identically. handle() resolves
$OMNI_HOME (fail-fast if unset when repo names need resolving).
- Exclude omni_worktrees / tests / fixtures from the scan (was double-counting
worktree copies + flagging deliberate test-fixture patterns).
- LOGIC_IN_NODE now skips string-literal/docstring AST lines (was flagging
docstring code examples).
Proof: real `onex skill compliance_sweep --dry-run` now returns
handlers_scanned=806, status=violations_found, identical to the __main__ path.
Regression test: tests/test_compliance_sweep_dispatch_resolution.py asserts a
no-arg dispatch yields handlers_scanned > 0.
* test(OMN-13514): make dispatch-resolution regression tests hermetic
CI checks out only omnimarket, so the prior no-arg/repos tests that relied on
the live sibling-repo workspace ($OMNI_HOME containing omnibase_core etc.)
scanned 0 handlers and failed. Rebuild them against a synthetic $OMNI_HOME
(monkeypatched) populated with fake default-repo dirs + handler files, and add
a fail-fast test asserting ValueError when OMNI_HOME is unset.
* fix(OMN-13521): exclude nested worktrees from doc-freshness md collection (#1358)
_collect_md_files walked the canonical clones with rglob and an exclude set
that did not skip nested git worktrees (.claude/worktrees, omni_worktrees).
Those are duplicate copies of the same source tree, so ~50% (omniclaude) to
~89% (omnimarket) of scanned docs were duplicates — inflating the (previously
quadratic) resolver cost and producing duplicate findings.
Add a path-segment-scoped exclusion set (_EXCLUDE_PATH_SEGMENTS) covering
.claude / worktrees / omni_worktrees plus generated/dependency trees. Matched
per path segment (not substring) so a legitimately named doc such as
docs/using_worktrees.md is NOT excluded — only an actual worktrees directory
segment is.
Measured on the real clones: omniclaude 690 -> 340 md (350 worktree dupes
removed), omnimarket 359 -> 92 (267 removed), omnibase_core ~unchanged.
TDD: tests/test_doc_freshness_worktree_exclusion.py pins exclusion of
.claude/worktrees and omni_worktrees (both *.md and claude_md_only modes) and
that a top-level doc merely named *_worktrees.md is kept.
* feat(OMN-13509): pin-hygiene acceptance corpus + SEA generation driver (omninode-generates-omninode) (#1360)
* feat(OMN-13509): SEA-generate pin-hygiene validator corpus + first-pass corpus acceptance
Marquee dogfood — omninode generates omninode. Adds the pin-hygiene acceptance
corpus (corpus_pin_hygiene) and drives it through the REAL HandlerGenerationConsumer
against the live local coder model (Qwen3.6-35B-A3B on .201:8000, routing resolved
from contract). The generated mechanical scanner was ACCEPTED on the FIRST attempt
because it passed the deterministic acceptance corpus in the hardened sandbox:
corpus_checked=true, corpus_passed=true, accepted=true (provenance JSON committed
under docs/evidence/OMN-13509/).
The corpus gates the pure pin-classification logic: it flags every off-dev-line
(orphan/unknown) sibling git pin across rev= / @rev / ?rev= / branch= syntaxes and
passes every ancestor pin, plus the non-sibling and version-range clean boundaries.
Git ancestry itself is resolved at the EFFECT boundary in the hand-hardened core
validator (OMN-13509), keeping the generated COMPUTE scanner pure.
The generated seed is hand-hardened and wired in omnibase_core (producer != owner).
* docs(OMN-13509): independent reproduction of the SEA pin-hygiene generation run
Second, independent run of the REAL generation path
(scripts/generation/drive_validator_generation.py --validator pin-hygiene —
the production HandlerGenerationConsumer against the live .201:8000 local-coder
model, NOT a fixture/echo harness). accepted=true, corpus_checked=true,
corpus_passed=true, usage_source=measured, model_id=Qwen3.6-35B-A3B,
routing_source=contract. attempt_count=3 and a DIFFERENT handler_source than the
original run (LLM non-determinism) — yet still accepted, because the corpus, not
the LLM, is the acceptance authority. Replayability proof for the marquee dogfood.
* docs(OMN-13509): redact local generation endpoints
* feat(OMN-13530): sweep operation_match contracts missing 'operation' + enforcement gate + headless auto_merge (#1361)
* feat(OMN-13530): add 'operation' to all operation_match handler contracts + wire enforcement gate
Sweep half of OMN-13530. RuntimeLocal._validate_routing (omnibase_core 0.46.0)
fails closed at startup ('handlers[i].operation is missing' / result=failed) for
any handler_routing entry routed by operation (operation_match, or absent strategy
that defaults into the same branch) that lacks a non-empty 'operation' field. The
6 live-broken automation skills (merge_sweep, ci_watch, auto_merge, build_loop,
coderabbit_triage, create_ticket) were in this set, so the fleet could not close
out its own PRs headless.
Changes:
- Add 'operation:' to every affected operation_match handler entry across 190
omnimarket node contracts. Single-handler nodes get a verb derived from the
node name; multi-handler nodes get a distinct operation per handler derived
from the handler/probe/adapter name (or the existing routing_key for the
legacy routing_key-shaped entries, where routing_key IS the operation).
- node_session_phase_{dispatcher,evaluator,orchestrator}: these carried a
duplicate top-level handler_routing key (malformed — PyYAML last-wins). Removed
the dead first block and added operation to the effective (event_model-bearing)
block.
- 9 golden-chain contract-routing tests asserted the exact handlers[] list and
now expect the added operation field.
- Wire enforcement (Rule 5): operation-match-requires-operation as a local
pre-commit hook + a required CI step (operation_match operation-field gate),
both running the canonical omnibase_core validator. Bump the omnibase_core git
pin to the OMN-13530 core branch so the validator module resolves.
Proof: runtime_sweep 322/322 entry points OK; full suite 8030 passed / 0 failed;
RuntimeLocal._validate_routing reports zero 'operation is missing' for all 7
automation contracts; merge_sweep/build_loop/create_ticket now print
'RuntimeLocal: routing graph:' headless (validation passes) where they previously
errored 'handlers[0].operation is missing'.
* feat(OMN-13530): make node_auto_merge_effect dispatch headless + re-pin core
The operation-field fix cleared 'handlers[0].operation is missing' for all 6
automation nodes, but node_auto_merge_effect still failed headless: RuntimeLocal
could not resolve an initial-payload model (handle() took multiple scalar params,
not a single typed payload, so neither contract.input_model, an event_model, nor
the handle-parameter-annotation fallback resolved).
- handle() now takes a single ModelAutoMergeInput payload (canonical single-payload
shape, matching node_dod_verify / ci_watch / build_loop / coderabbit_triage /
create_ticket); unpacks the 7 fields internally — body logic unchanged.
- contract.yaml: add top-level input_model + an inline event_model on the
operation_match entry pointing at ModelAutoMergeInput (mirrors node_dod_verify).
- Update 9 handler-test call sites to pass ModelAutoMergeInput.
- New tests/test_golden_chain_auto_merge_effect.py: asserts operation present,
RuntimeLocal._validate_routing clean, initial-payload model resolvable, and the
single-payload merge chain — satisfies the golden-chain-coverage-gate.
- Re-pin omnibase_core to 287511f2 (validator-bearing branch) in pyproject + uv.lock.
dod_evidence: RuntimeLocal._validate_routing returns [] for all 6 nodes;
payload_resolvable=True for all 6; 'onex skill auto_merge' no longer reports
'operation is missing' and resolves ModelAutoMergeInput from contract.input_model.
* test(OMN-13530): add operation-field golden-chain coverage
* fix(OMN-13530): align overseer operation routing
* chore(OMN-13530): refresh OCC evidence event
* feat(OMN-13535): propagate per-tier metered cost across escalation to delegation projection (#1362)
* feat(OMN-13535): propagate per-tier metered cost across escalation to delegation projection
On an escalated delegation, the metered tier's call really runs and reports real
tokens, but the orchestrator terminal recorded only the FINAL accepted tier
(often free) — the rejected/failed metered tier's tokens/cost were reset on
escalation and dropped, so delegation_events carried cost_usd=0.0 despite a real
metered cloud call. This blocked the OMN-13408 metered-tier cost_usd>0 proof.
Fix (orchestrator + projection, code-only):
- ModelDelegationEscalationAttempt now carries per-attempt prompt_tokens /
completion_tokens / cost_usd. Each attempted tier is priced once (the same
recompute_actual_cost_and_savings the projection uses) and stamped into
escalation_history before the inference state resets for the next tier.
- The orchestrator accumulates non-terminal attempt spend (banked only on the
ESCALATION branches, never on the terminal attempt — no double count) and the
terminal reports final_tier_cost + cumulative_prior_attempt_cost as cost_usd /
cumulative_attempt_cost, with savings = counterfactual - total_spend.
- node_projection_delegation carries escalation_history through and, on the
re-priced (completed) path, adds the per-attempt costs to the re-priced final
tier so an accepted-on-free escalation that burned metered budget reports the
real cost_usd>0 (failure path keeps the orchestrator's already-total cost_usd).
Net: an escalated metered delegation now produces a delegation_events row with
tokens>0 + cost_usd>0 reflecting the metered tier, even when the final accepted
tier is free.
Cites OMN-13535 (relates OMN-13408, OMN-13532).
dod_evidence:
- New unit suite tests/unit/delegation/test_escalation_metered_cost_reaches_projection_omn13535.py
(6 tests): metered cheap_cloud rejected -> escalate -> free 'local' accepted
yields terminal cumulative_attempt_cost>0 + projection cost_usd>0; savings
reconciles to counterfactual - total; all-tiers-failed terminal counts the
metered tier once (no double count); non-escalated row unchanged.
- Affected scope: 428 passed (delegation + projection_delegation suites).
- mypy --strict clean (2473 files); ruff + pre-commit (all hooks) clean.
- Live metered re-proof needs a dev-lane redeploy — operator-gated follow-up.
* test(OMN-13535): pin bifrost routability so metered→free escalation tests pass in CI
The 4 escalation-driving tests asserted state==ROUTED after a quality-gate
rejection on cheap_cloud, expecting escalation to the free 'local' tier. Tier
routability depends on each backend resolving a non-empty endpoint URL; the
local backends resolve theirs from ambient BIFROST_LOCAL_* endpoint env vars
(set on a developer machine, absent in CI). With those unset in CI the 'local'
tier was unroutable, next_eligible_tier('cheap_cloud') found no routable next
tier, and the workflow terminated FAILED instead of ROUTED.
Apply the shared frontier_unconfigured_bifrost fixture (conftest) to the test
class so routability is pinned to a deterministic bifrost contract — local +
cheap_cloud carry resolvable endpoints, the frontier ceiling does not —
independent of ambient env. Verified failing→passing by reproducing the CI
condition locally (BIFROST_LOCAL_* stripped): 6/6 pass; full delegation dir
376 passed/1 skipped with env stripped.
* fix(OMN-13538): sweep nodes no longer false-clean — share $OMNI_HOME default-scope resolution into handlers + fail-loud on unresolvable scope (#1363)
Systemic Rule-5 fix. Invoked the operator-canonical no-arg way (onex skill
<name> -> RuntimeLocal dispatch), these enforcement-gate sweeps scanned 0 repos
and returned status=clean — a trust-defeating false-clean. Each node's
_DEFAULT_REPOS + repo-name->path resolution lived only in __main__.py, which the
dispatch path never executes. Mirrors the OMN-13514 compliance_sweep fix.
New shared leaf module omnimarket.nodes.sweep_scope owns the canonical default
repo set + resolver, shared by both __main__ and the dispatch handlers so they
scan identically. Fail-loud contract: empty AND unresolvable scope -> error /
raise, never clean (Rule 5: a gate that silently passes is worse than no gate).
- coverage_sweep: add repos field; resolve default scope in handle(); empty
unresolvable -> status=error (was repos_scanned=0, status=clean).
- aislop_sweep: add repos field; resolve default scope in handle(); empty
unresolvable -> status=error (was loop over empty target_dirs -> clean).
- gap_compute (OMN-13534): _resolve_repo_roots now enumerates $OMNI_HOME default
repos instead of parents[5] (which on the deployed path resolved a python3.12
version-token root); never adopts a non-omni_home site-packages root.
- duplication_sweep D3: subprocess timeout 60s -> 240s (real walltime ~110s) and
a real TimeoutExpired now FAILs the check instead of a quiet WARN(0); empty
omni_home -> overall_status=ERROR instead of a PASS over a missing scan root.
Tests: tests/test_sweep_dispatch_scope_resolution.py — no-arg dispatch resolves
the default repo set and scans (repos_scanned>0), forced-empty-unresolvable
scope returns error/raises, D3 timeout maps to FAIL. Updated the gap golden-chain
default-scope test to assert the corrected $OMNI_HOME contract.
dod_evidence:
- before: coverage/aislop no-arg dispatch -> repos_scanned=0, status=clean
- after: coverage/aislop no-arg -> repos_scanned=8; gap repos_in_scope = 8
canonical repos (not python3.12); fail-loud -> status=error/ERROR; D3
timeout -> FAIL. 58 targeted tests green; node suite 2452 passed; pre-commit
exit 0; mypy --strict clean.
Does NOT edit omnibase_infra/cli/skill_mapping.yaml (registry/field side owned by
OMN-13511). compliance_sweep (OMN-13514) and data_flow_sweep (OMN-13534) out of
scope here.
Ticket: OMN-13538 (NODE-HANDLER default-scope side); gap credited to OMN-13534.
* fix(OMN-12951): port EvidenceEvaluator + SideEffectObserver from typing.Protocol to ABC (#1364)
infra 0.37.0 crash-loops on bootstrap when wire_from_manifest tries to
instantiate a typing.Protocol handler_cls zero-arg (Protocols cannot be
instantiated). Port EvidenceEvaluator and SideEffectObserver from
@runtime_checkable Protocol to abc.ABC + @abc.abstractmethod so the
runtime can safely inspect them without crashing.
- NullEvidenceEvaluator and NullSideEffectObserver now inherit from the
respective ABCs (isinstance checks preserved).
- Direct instantiation of the abstract base classes raises TypeError from
ABC (not from typing.Protocol internals), which is caught by the infra
0.38.x quarantine guard.
- Added 6 regression tests: Protocol-check, abstract-instantiation,
concrete-subclass verification for both classes.
- Updated contract.yaml comments and dod_evidence description.
* fix(OMN-13539): re-lock omnibase_infra pin past OMN-13405 core-vendor fix (#1365)
Re-lock omnimarket dev's omnibase-infra pin from f2533f5a (v0.38.3) to the
current omnibase_infra origin/dev HEAD 7e52d5b0 (v0.38.4), which contains the
OMN-13405 core-vendor Dockerfile fix 71848f81a4. The stale pin predated that
fix, so the dev runtime workspace build vendored an enum-less omnibase_core and
the delegate-skill orchestrator ImportError'd on ModelPremiumCounterfactual at
bootstrap.
Pure dependency-pin re-lock: only the omnibase-infra SHA in pyproject.toml
([tool.uv.sources] rev= and [tool.uv] override-dependencies) and uv.lock change.
No source, version specifier, migration, schema, endpoint, or env-for-model
change. Other sibling pins (core/compat/memory/onex-change-control) unchanged.
Evidence-Ticket: OMN-13539
* fix(OMN-12764): golden-chain regression — delegate-skill test task terminalization (#1366)
Proves that delegate-skill commands with task_type='test' always terminate
after the delegation orchestrator processes an inference response, never stall.
The live dev runtime showed that test-task correlations published successful
inference-response events but the delegation-completed / delegation-failed
terminal was never emitted, leaving delegation_events with row_count=0.
New test file covers 6 scenarios:
- Active path: response with @pytest.mark.unit passes quality gate DoD check
- Inactive path: response missing @pytest.mark.unit fires deterministic TASK_MISMATCH
(terminal emitted as delegation-failed, not a stall)
- ValueError: invalid task_type raises ValidationError from the wire model
- Bus-backed round-trip (2 variants): RuntimeDelegationDispatchPort subscribes
before publishing, receives completed and failed terminals via EventBusInmemory,
confirming the wait=True path for test task type works end-to-end
Evidence: uv run pytest tests/test_golden_chain_delegate_skill_test_task_terminalize_omn12764.py -q → 6 passed
* feat(OMN-12884): add node_projection_replay_check_compute — replay/idempotence projection dedupe checks (#1367)
* feat(OMN-12884): add node_projection_replay_check_compute — replay/idempotence projection dedupe checks
Implements the Phase 7 guardrail from the dev-stability loop closure plan:
- EnumReplayStatus with replay-proven / runtime-observed / blocked / superseded taxonomy
- HandlerProjectionReplayCheck classifies (correlation_id, table) pairs by replaying
events through the Kafka partition/offset analysis logic
- 17 tests: exact-replay dedupe proof, superseded-offset detection, mixed batch,
InmemoryDatabaseAdapter ON CONFLICT idempotence fixtures for both delegation_events
and generation_events
- contract.yaml, metadata.yaml, pyproject.toml entry-point registration
- All pre-commit hooks pass; mypy strict clean
* test(OMN-12884): update market node dogfood inventory
* feat(OMN-13540): delegation regression suite (golden-task corpus + Layer-1 CI gate + nightly runner) (#1369)
* feat(OMN-13540): delegation regression suite (golden-task corpus + Layer-1 CI gate + nightly runner)
Adds a fixed, versioned set of delegation tasks with BEHAVIORAL assertions so
delegation regressions are caught mechanically, not by chance. Two layers:
Layer 1 (contract/unit, PRE-MERGE CI gate, runs on every PR, no live model):
U1 orchestrator contract loads + handler imports cleanly (OMN-13539 ImportError)
U2 every routing tier resolves to a complete provider+endpoint+model+api_key_ref
U3 ModelDelegateSkillRequest validates each allowed task_type, rejects unknown
U4 terminal/projection schema carries model_name + token + cost (OMN-13535)
Layer 2 (live golden tasks, NIGHTLY against the stability-test lane):
- declarative corpus.yaml (I1-I9) — one row per case, data not code
- runner publishes each case to the live bus via the delegate-skill command
topic, reads delegation_events, asserts the expected behavioral block
(tier reached, quality gate, terminal, token/cost, escalation/exhaustion)
- scoreboard artifact per run
- new scheduled GitHub Actions workflow on a self-hosted runner with .201 lane
access; fails on hard (non-xfail) assertion breaks
Assertions encode INTENDED behavior. Known-broken cases are xfail with the
tracking ticket so the nightly is actionable: I2/I8 (OMN-13140 escalation/
exhaust), I4 (OMN-13408 metered cost), I9 (OMN-13535 empty terminal data).
The assertion text still encodes the correct expectation — the case goes green
only when the real fix lands.
Test-only + nightly-CI-workflow change; no runtime node/source/migration/schema/
endpoint/env change.
* fix(OMN-13540): remove IP-literal fallbacks from nightly workflow (leak gate)
The Leaked Literals Gate scans CI YAML and rejected the 192.168.* fallbacks in
delegation-regression-nightly.yml. Wire lane addresses from repo vars only (no
IP literal in CI config); an unset var resolves empty and the runner falls back
to its own annotated stability-test defaults. runner.py now treats an unset OR
empty lane env var as 'use the annotated default' via _env_or so the empty-var
pass-through still resolves a real host.
* fix(OMN-12994): extract usage_source from usage_normalized.source for infra wire format (#1368)
* fix(OMN-12994): extract usage_source from usage_normalized.source for infra wire format
The ContractLlmCallMetrics wire format (emitted by HandlerLlmOpenaiCompatible
via ServiceLlmMetricsPublisher to onex.evt.omniintelligence.llm-call-completed.v1)
nests usage provenance as usage_normalized.source ("api"/"estimated"/"missing")
with no top-level usage_source field. build_llm_call_metrics_row only looked
for a flat usage_source key, so every row from the exp0/generation-consumer
path landed with usage_source=MISSING and prompt_tokens=0 in the ab-compare
projection.
Fix: when usage_source/usageSource are absent at the top level, fall back to
data["usage_normalized"]["source"] before defaulting to MISSING. Top-level
field still wins (backward compatible). Added 5 TDD tests covering the
ContractLlmCallMetrics wire shape ("api"→API, "estimated"→ESTIMATED,
"missing"→MISSING, top-level priority, neither→MISSING).
* chore(OMN-12994): retrigger gates after OCC merge
* fix(OMN-12994): refresh omnibase_core git pin
* chore(OMN-12994): retrigger gates after deploy evidence
* chore(OMN-12994): retrigger gates after OCC merge
* fix(OMN-13541): make code_review a first-class delegation task class (#1370)
* fix(OMN-13541): make code_review a first-class delegation task class
Pairs with omnibase_core PR #1318 (adds code_review to the wire DTO task_type
Literal). Before this, code_review was accepted at the consumer-facing surface
(node_delegate_skill_orchestrator.allowed_task_types + the claude/codex adapter)
but the downstream bus consumer rejected it (Literal) and, even past that, had no
task-class contract entry and only the single local-coder model in routing_tiers
use_for — a Pattern-B timeout with zero inference.
- configs/task_class_contracts.v1.yaml: add a code_review task class (mirrors
review with code-review DoD heuristics) + task_model_override, so the quality
gate's required-bar authority resolves it instead of raising.
- configs/routing_tiers.yaml: add code_review to the cheap_cloud glm-5.2 use_for
so escalation_policy.tier_order [local, cheap_cloud] has a real cloud fallback
(removes the single-local-backend strand risk).
- models/delegation/wire/model_delegation_request.py: add code_review to the
omnimarket-local wire DTO Literal (kept in sync with core).
- ports/port_local_delegation_dispatch.py: add the code_review system prompt for
the standalone local CLI path.
- tests: focused unit tests proving the task class resolves a required_bar and
routing serves code_review on local + cheap_cloud.
Live dev-lane proof (docs/evidence/OMN-13541/root-cause.md): a fresh code_review
delegation terminalized with real inference (model=glm-5.2, tokens 61/613).
Evidence-Ticket: OMN-13541
* chore(OMN-13541): retrigger gates after OCC omnimarket evidence
* chore(OMN-13541): retrigger gates after OCC merge
* fix(OMN-13544): reconcile golden_chain expected_fields to real projection columns (#1371)
Five OMN-12660 WS-G error-chain regression guards in golden_chains.yaml
referenced delegation_events columns that never existed in the table, so the
sweep (which checks `field not in row` against projected DB-row keys) could
only ever FAIL them — config drift, not a real signal.
Reconciled against the deployed node_projection_delegation migrations:
- d3_local_routing: base_url/model -> model_name
- d1_d2_scaffold: node_name/contract_passed/content -> quality_gate_passed/response_text
- d4_blank_content: content/model_used -> response_text/model_name
- d9_wheel_module: node_startup_ok -> (correlation_id presence is the guard)
- f1_publish_loop: published_at -> timestamp
Adds tests/test_golden_chain_expected_fields_columns.py: a guard that asserts
every expected_field names a real projection-table column (column sets pinned
from the omnibase_infra forward migrations), so this drift class cannot recur.
Updates the two sweep fixtures that built projected_rows with the old names.
* test(OMN-13545): lock pattern_learning/routing/evaluation golden-chain emit wiring (#1373)
Investigation of the three golden-chains reporting 0 head-topic messages on
the dev lane (pattern_learning, routing, evaluation) found NO missing or
broken publish path inside omnimarket:
- routing (llm-routing-decision.v1) and evaluation (session-outcome.v1):
head topics ARE declared emit-daemon fan-out targets in the canonical
event registry (topics.yaml, OMN-13146). Producers are the omniclaude
hooks emitting via the daemon. Dev-lane silence is a runtime/IPC condition
(daemon not connected to the dev broker), tracked by the emit_daemon ->
thin-publish EFFECT migration (OMN-13213).
- pattern_learning (pattern-stored.v1): produced direct-to-bus by the
omniintelligence node_pattern_storage_effect EFFECT node; silent on dev =
that intelligence node not deployed/active on the dev lane.
The end-to-end omnimarket wiring is correct. The silent failure mode that was
unguarded is *drift* between the golden-chain head topic, the registry fan-out
that produces it, and the projection that consumes it. The routing chain in
particular has two easily-confused projections (node_projection_llm_routing ->
llm_routing_decisions is the correct consumer; node_projection_routing_decision
-> agent_routing_decisions is the separate OMN-13122 registration chain).
This regression test pins each of the three head topics to its declared
omnimarket-visible emit/consume path so registry/contract drift fails CI
instead of silently going dark on the dev lane. Live publish verification is
deferred to the dev-lane redeploy (intelligence node + emit daemon).
* fix(OMN-13548): route malformed projection events to canonical DLQ (D-03) (#1372)
* fix(OMN-13548): route malformed projection events to canonical DLQ (D-03)
HandlerProjectionDelegation / HandlerProjectionSavings runners caught a
ValidationError (and several missing-required-field checks) on malformed
delegation/savings events, logged at WARNING, and dropped them silently — no
DLQ, no failure event, no terminal. A malformed event vanished from the bus
with no recoverable trace.
Now every such drop emits a DURABLE failure signal ON THE BUS: the offending
envelope is routed to the contract-declared DLQ topic (event_bus.dlq_topics),
carrying the event's correlation_id + the validation failure reason.
- New shared surface omnimarket.projection.dlq (route_to_dlq + canonical
envelope) mirrors the existing node_intent_event_consumer_effect DLQ pattern
(original_message / failure_reason / retry_count / failed_at / handler);
topic is read from contract.yaml, never invented inline.
- Contracts declare onex.dlq.omnimarket.projection-delegation-malformed.v1 and
onex.dlq.omnimarket.projection-savings-malformed.v1 under event_bus.dlq_topics.
- Savings runner gained a lazy Kafka producer (it had none) so its DLQ path has
a publisher in the live runtime.
- CI/pre-commit guard scripts/ci/check_projection_dlq_path.py fails any
projection handler that catches ValidationError without a DLQ route.
- Test tests/test_projection_malformed_dlq_omn13548.py proves a deliberately
malformed event yields a DLQ row for its correlation_id (both runners).
OMN-13548
* fix(OMN-13548): declare aiokafka dep on node_projection_savings metadata
The savings runner gained a lazy Kafka producer for its DLQ path (OMN-13548), so
ONCP node metadata dependency scope now requires aiokafka be declared in the
node's metadata.yaml. Mirrors node_projection_delegation.
OMN-13548
* fix(OMN-13550): correct handler class names in node contracts to match real symbols (#1374)
A dev-HEAD redeploy crash-looped the runtime at boot with
CLASS_NOT_FOUND (HANDLER_LOADER_011): node_baseline_capture/contract.yaml
named handler 'probe_db_row_counts' which does not exist as a symbol in its
module — the real class is 'ProbeDbRowCounts'. OMN-12408's fail-loud (merged
2026-06-23) now correctly crashes on this instead of silently skipping the
contract, blocking the whole dev runtime boot.
Swept all 370 handler_routing entries across every omnimarket node contract
and fixed every name that did not resolve via hasattr(module, name) — the
exact check the runtime loader performs:
- node_baseline_capture: 6 probes -> Probe* PascalCase classes
- node_data_verification: datasource_postgres -> PostgresDataSource
- node_dispatch_worker: DispatchRecordWriter -> write_dispatch_record
- node_environment_health_scanner: 7 prober_* -> probe_* functions
- node_platform_readiness: dimension_checks -> run_all_dimensions
- node_process_watchdog: 3 entries -> real targets/builder
- node_routing_policy_engine: 2 -> determine_retry_type, extract_task_shape
- node_session_post_mortem: 2 -> read_friction_events, collect_pr_status
- node_skill_overseer_verify_orchestrator: -> handle_skill_requested
Adds tests/test_handler_class_existence_omn13550.py which parametrizes over
every contract handler entry and asserts the symbol exists (mirrors the
OMN-12408 runtime invariant locally; the pinned omnibase_infra in this repo's
venv predates that enforcement).
* fix(OMN-13320): stale-run cleanup must protect the current PR head run (#1376)
node_pr_lifecycle_fix_effect cleanup now force-cancels obsolete-head
pull_request workflow runs ONLY, never the run tied to the current PR
head SHA (cancelling it just re-triggers the checks the merge waits on).
HTTP 409 ("Cannot cancel a workflow re-run that has not yet queued") is
classified as stale metadata: skipped, never retried, never raised.
- GitHubApiError now carries status_code so callers classify 409 by code,
not message string-matching.
- GitHubCliAdapter.cancel_obsolete_runs enumerates pull_request runs for
the PR branch, protects the current head_sha, force-cancels the rest.
- Tests assert the current head run id survives cleanup, the 409 path is
skipped (next obsolete run still cancelled), and non-409 errors fail loud.
Enforcement Map F4. Relates OMN-9288.
* feat(OMN-13318): org-wide open-PR census as mandatory sweep-done gate (#1377)
* feat(OMN-13318): org-wide open-PR census as mandatory sweep-done gate
Add an org-wide open-PR census to node_pr_lifecycle_inventory_compute and
gate the orchestrator's sweep-done report on it. The overnight merge sweep
could falsely look complete when a repo-by-repo inventory missed an open PR
in another repo (e.g. omnibase_infra#2043 still open after two merged).
inventory_compute:
- collect_org_wide_open_prs() runs the GitHub search equivalent of
'gh api /search/issues?q=org:OmniNode-ai is:pr is:open', returning
ModelOrgWideOpenPrInventory(open_count, remainders, query_failed).
- ModelPrInventoryOutput.org_wide_open carries the census.
- Fail-closed: a failed/invalid query is never reported as done.
orchestrator:
- captures the census during INVENTORYING.
- _apply_org_wide_done_gate downgrades final_state COMPLETE -> NOT_DONE
while open_count > 0 (or the census failed), surfacing the exact
remainders. result.json reports status=not_done with the blockers.
DoD test: seed one open PR -> NOT_DONE listing the remainder; close it ->
COMPLETE. Covered at both node and orchestrator level.
Contract/metadata bumped to 1.1.0.
Closes OMN-13318
* fix(OMN-13318): reword docstrings to avoid STUB marker tripping stub-impl lint
* test(OMN-13540): reconcile delegation corpus xfail against 2026-06-24 dogfood (#1375)
Flip I9 (cross-cutting model_name+tokens on every completed row) to
expected-green: the redeploy dogfood (verifier != runner,
docs/evidence/2026-06-24-redeploy-dogfood/results.md) showed all four
completed delegation_events rows carried non-empty model_name AND
tokens_input/_output > 0. The empty-terminal-data regression (OMN-13535)
is no longer reproduced on the redeployed dev lane.
Keep I8 (all-tiers-exhaust) xfail: the dogfood proved only the passing
research + code_review paths, never an exhaustion scenario. The
escalation-exhaustion terminal fix (OMN-13543) is still open and the
graceful all-tiers-exhaust path (OMN-13140) remains unproven; sharpen the
reason/ticket so the nightly stays actionable.
I5 (research) + I6 (code_review) were already expected-green (no xfail);
the dogfood CONFIRMED both terminalize with real model_name + tokens
(OMN-13541 + OMN-13546 forwarder fix). No change needed.
Test-only data edit (corpus.yaml). Layer-1/pure-logic suite: 21 passed.
* fix(OMN-13319): count only PR-associated pull_request runs toward required contexts (#1378)
Triage/inventory now credit a required CI context only from a PR-associated
`pull_request` (or `pull_request_target`) run, never a manual
`workflow_dispatch` "CI Summary". A green workflow_dispatch run is dropped
from the ci_passing computation, so a PR whose only green is a manual dispatch
stays not-green and arm refuses. Eventless legacy status contexts remain
PR-associated. The node trusts the PR-associated run conclusion, not the
(possibly stale) statusCheckRollup row.
Enforcement Map F3 / handoff #4 (omnibase_core#1280).
OMN-13319
* fix(OMN-13553): golden_chain_sweep fail-closed on empty chains + skill-dispatch loads registry (#1379)
The canonical skill-dispatch path `onex skill golden_chain_sweep` returned a
vacuous green: `overall_status: pass` over `chains_total: 0`. RuntimeLocal
builds the request as `GoldenChainSweepRequest()` (only the runtime-injected
correlation_id), which defaulted chains=[], and the reducer computed PASS over
the empty set. An operator/CI gate read 'all golden chains healthy' while zero
chains were validated.
Two pure-COMPUTE fixes in handler_golden_chain_sweep.py:
1. chains field defaults to the packaged golden_chains.yaml registry
(deterministic config resolution at construction, NOT live runtime I/O), so
the skill-dispatch path validates a re…
Summary
Dependency
Evidence
Closeout linkage
Summary by CodeRabbit
Release Notes
New Features
context_pack_hashfor downstream readback/ROI.Bug Fixes
context_pack_hashis propagated through inference, compliance/repair, and projection event paths, while preserving existing terminal evidence when later events provide blanks.Tests
context_pack_hashis correctly persisted, defaulted to empty when omitted, and verified in inference prompt injection.Evidence-Source: OCC#2897
Evidence-Ticket: OMN-13407