Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a14b0ea
feat(OMN-12960): single transport-envelope unwrap predicate + dispatc…
jonahgabriel Jun 11, 2026
eac84ed
feat(OMN-12957): runtime-profile registry + unregistered/no-consumer-…
jonahgabriel Jun 11, 2026
2defabe
fix(OMN-12961): reject typing.Protocol handler targets in ServiceHand…
jonahgabriel Jun 11, 2026
309d89f
feat(OMN-13026): transport-mock lint validator — bare AsyncMock/Magic…
jonahgabriel Jun 12, 2026
8ee9900
fix(OMN-13061): hard-fail on contract_sha256=None in OCC eligibility …
jonahgabriel Jun 12, 2026
e72a73e
feat(OMN-13026): protocol-conformance tests per Protocol + portable _…
jonahgabriel Jun 12, 2026
6d0c211
feat(OMN-12818): url-authority ratchet gate — ValidatorUrlAuthority i…
jonahgabriel Jun 12, 2026
ff4be84
feat(OMN-13091): ModelArtifactRef + ModelSkillResult[T] typed receipt…
jonahgabriel Jun 12, 2026
ae8793b
feat(OMN-13093): minimal content-addressed ArtifactStore slice (Phase…
jonahgabriel Jun 12, 2026
35fce1b
feat(OMN-13098): skill-dispatch receipt-mode validator (Phase 4b) (#1…
jonahgabriel Jun 13, 2026
9dd6f61
docs(OMN-13182): refresh omnibase_core documentation (#1241)
jonahgabriel Jun 16, 2026
0babc78
refactor(OMN-13189): delete dead ModelOnexEnvelopeV1 — completes OMN-…
jonahgabriel Jun 17, 2026
a84bb24
refactor(OMN-13192): migrate sole live ModelOnexEnvelope constructor …
jonahgabriel Jun 17, 2026
5f35876
chore(OMN-13187): refresh core dependency pins (#1246)
jonahgabriel Jun 17, 2026
b7ed3f0
refactor(OMN-13194): re-annotate envelope_validator.py from ModelOnex…
jonahgabriel Jun 17, 2026
20dcc61
chore(OMN-13195): bump validate-boundaries action pin (#1249)
jonahgabriel Jun 17, 2026
e5400c8
feat(OMN-12804): non-model URL contract authority — integration catal…
jonahgabriel Jun 17, 2026
e9bb51a
refactor(OMN-13196): delete ModelOnexEnvelope (B4) + retarget validat…
jonahgabriel Jun 17, 2026
d76f02d
chore(OMN-13200): bump omnibase_core to v0.45.0 (B6 — envelope-dedup …
jonahgabriel Jun 17, 2026
3ff83b3
refactor(OMN-13197): remove dead onex.runtime.intents hardcoded topic…
jonahgabriel Jun 17, 2026
7db9d45
feat(OMN-13209): promote runtime-deployment wire DTOs (EnumRuntimeLan…
jonahgabriel Jun 17, 2026
d215fe1
feat(OMN-13130): UI contract primitives + EnumEmptyStateReason in omn…
jonahgabriel Jun 17, 2026
9d0de1d
feat(OMN-13132): Contract Graph IR + read-only import (#1256)
jonahgabriel Jun 18, 2026
00be641
fix(OMN-13214): port hardened arm+update-branch+enqueue+verify auto-m…
jonahgabriel Jun 18, 2026
c281fbb
feat(OMN-13219): canonical-inference enforcement gate (pre-commit + r…
jonahgabriel Jun 18, 2026
58fd22e
chore(OMN-13131): promote core dev->main (supersede prior squash dive…
jonahgabriel Jun 18, 2026
2ccde09
ci(OMN-13131): adopt main's permissive main-target-guard on the promo…
jonahgabriel Jun 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 130 additions & 19 deletions .github/workflows/auto-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@
# targets. The main job depends on pre-check and is skipped entirely when
# skip=true. A concurrency group per PR cancels redundant queued runs so
# only the most-recent check_suite completion for a given PR actually runs.
#
# OMN-13214: Arming auto-merge is NOT the same as enqueuing. During the
# 2026-06-17 dev wave, CLEAN + all-required-green + armed PRs sat OUTSIDE the
# merge queue for hours and each needed a manual enqueuePullRequest to land.
# Two fixes here: (1) the "Enable auto-merge" step previously passed bare
# `--auto --squash`, but dev is queue-controlled, so an explicit method is
# REJECTED ("The merge strategy for dev is set by the merge queue") and the PR
# is NEVER enqueued. Use bare `--auto` and let the queue pick the method.
# (2) The old "Force enqueue" step did not verify the PR actually entered the
# queue. This workflow now (a) arms bare --auto, (b) runs gh pr update-branch
# first on strict-update repos when the PR is BEHIND, (c) explicitly calls
# enqueuePullRequest, and (d) VERIFIES the PR actually entered the queue —
# failing loudly if a green+armed PR is not enqueued, so the gap surfaces
# instead of stalling silently. The classify/verify decision logic is
# unit-tested in scripts/ci/merge_queue_enqueue.py.
name: Auto-Merge

on:
Expand Down Expand Up @@ -198,20 +213,23 @@ jobs:
PR: ${{ needs.pre-check.outputs.pr }}
run: |
set -euo pipefail
# "already enqueued" race is benign — treat as success.
# All other failures surface loudly (no silent continue-on-error).
if output=$(gh pr merge "$PR" --repo "$GH_REPO" --auto --squash 2>&1); then
# OMN-13138/13214: dev is queue-controlled — the merge method is set by
# the merge queue, so passing an explicit --squash is rejected ("The
# merge strategy for dev is set by the merge queue") and the PR is NEVER
# enqueued (sits CLEAN + auto-merge armed forever). Use bare --auto and
# let the queue pick the method.
# OMN-13214: arming alone does not enqueue — the explicit enqueue +
# verify step below closes that gap. Do NOT exit here on success.
if output=$(gh pr merge "$PR" --repo "$GH_REPO" --auto 2>&1); then
echo "auto-merge enabled: $output"
exit 0
fi
if echo "$output" | grep -qiE "already enqueued|already queued|auto-merge already enabled"; then
elif echo "$output" | grep -qiE "already enqueued|already queued|auto-merge already enabled"; then
echo "auto-merge not newly enabled (expected): $output"
exit 0
else
echo "auto-merge failed: $output"
exit 1
fi
echo "auto-merge failed: $output"
exit 1

- name: Force enqueue to merge queue
- name: Enqueue armed PR and verify it entered the queue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
Expand All @@ -220,18 +238,111 @@ jobs:
set -euo pipefail
OWNER="${GH_REPO%/*}"
NAME="${GH_REPO#*/}"
PR_ID=$(gh api graphql -f query='{repository(owner:"'"$OWNER"'",name:"'"$NAME"'"){pullRequest(number:'"$PR"'){id}}}' --jq '.data.repository.pullRequest.id')

classify() {
python3 scripts/ci/merge_queue_enqueue.py classify \
--pr-json "$1" --strict-update "$2"
}

# Is the target (default) branch strict-update? On strict repos a BEHIND
# PR cannot enqueue until its branch is updated.
DEFAULT_BRANCH="$(gh repo view "$GH_REPO" --json defaultBranchRef --jq '.defaultBranchRef.name')"
STRICT="$(gh api "repos/${GH_REPO}/branches/${DEFAULT_BRANCH}/protection/required_status_checks" \
--jq '.strict' 2>/dev/null || echo "false")"
[ "$STRICT" = "true" ] || STRICT="false"
echo "strict-update on '${DEFAULT_BRANCH}': ${STRICT}"

fetch_pr() {
# isInMergeQueue is only on the GraphQL PullRequest object, NOT a
# `gh pr view --json` field, so fetch PR state via GraphQL.
# shellcheck disable=SC2016 # $owner/$name/$number are GraphQL vars, not shell vars
gh api graphql \
-f owner="$OWNER" \
-f name="$NAME" \
-F number="$PR" \
-f query='
query($owner:String!, $name:String!, $number:Int!) {
repository(owner: $owner, name: $name) {
pullRequest(number: $number) {
isInMergeQueue
mergeStateStatus
mergeable
autoMergeRequest {
enabledAt
}
}
}
}' \
--jq '.data.repository.pullRequest'
}

PR_JSON="$(fetch_pr)"
ACTION="$(classify "$PR_JSON" "$STRICT")"
echo "classified action: ${ACTION}"

case "$ACTION" in
already_queued)
echo "PR #${PR} already in the merge queue — nothing to do."
exit 0
;;
wait)
echo "PR #${PR} is not enqueueable yet (BLOCKED/DIRTY/DRAFT/UNKNOWN); will re-fire on next event."
exit 0
;;
blocked)
echo "::warning::PR #${PR} in an unrecognised merge state: $(printf '%s' "$PR_JSON" | jq -r '.mergeStateStatus'). Not enqueuing."
exit 0
;;
update_branch_then_enqueue)
echo "PR #${PR} is BEHIND on a strict-update repo — updating branch before enqueue."
if ! gh pr update-branch "$PR" --repo "$GH_REPO" 2>&1; then
echo "::warning::update-branch reported no change or a benign error; continuing."
fi
echo "update-branch issued; the resulting check run re-fires this workflow when CI is green."
exit 0
;;
enqueue)
: # fall through to enqueue below
;;
*)
echo "::error::unexpected action '${ACTION}' from classifier"
exit 1
;;
esac

PR_ID="$(gh api graphql -f query='{repository(owner:"'"$OWNER"'",name:"'"$NAME"'"){pullRequest(number:'"$PR"'){id}}}' --jq '.data.repository.pullRequest.id')"
if [ -z "$PR_ID" ] || [ "$PR_ID" = "null" ]; then
echo "could not resolve PR id"; exit 0
echo "::error::could not resolve PR node id for #${PR}"
exit 1
fi
if output=$(gh api graphql -f query='mutation($prId:ID!){enqueuePullRequest(input:{pullRequestId:$prId}){mergeQueueEntry{position}}}' -f prId="$PR_ID" 2>&1); then
echo "enqueued: $output"

# shellcheck disable=SC2016 # $prId is a GraphQL variable, not a shell var
if enq_out=$(gh api graphql -f query='mutation($prId:ID!){enqueuePullRequest(input:{pullRequestId:$prId}){mergeQueueEntry{position}}}' -f prId="$PR_ID" 2>&1); then
echo "enqueue issued: ${enq_out}"
else
# Tolerate only known benign states.
if echo "$output" | grep -qiE "already (in|enqueued|queued)|merge queue (is )?not enabled|not mergeable|required status check"; then
echo "enqueue attempt result (tolerated): $output"
if echo "$enq_out" | grep -qiE "merge queue (is )?not enabled|does not have a merge queue|merge_queue_not_enabled"; then
echo "repo has no merge queue — arming auto-merge is sufficient: ${enq_out}"
exit 0
fi
echo "enqueue failed: $output"
exit 1
if echo "$enq_out" | grep -qiE "already (in|enqueued|queued)|in the merge queue"; then
echo "benign enqueue race (already queued): ${enq_out}"
else
echo "::error::enqueuePullRequest failed for #${PR}: ${enq_out}"
exit 1
fi
fi

# OMN-13214 ENFORCEMENT: verify the PR actually entered the queue.
# Poll briefly — GitHub may take a few seconds to reflect the entry.
for attempt in 1 2 3 4 5; do
sleep 6
PR_AFTER="$(fetch_pr)"
if python3 scripts/ci/merge_queue_enqueue.py verify --pr-json "$PR_AFTER"; then
echo "VERIFIED: PR #${PR} is in the merge queue."
exit 0
fi
echo "not yet in queue (attempt ${attempt}/5); state=$(printf '%s' "$PR_AFTER" | jq -r '.mergeStateStatus')"
done

echo "::error::PR #${PR} is green + armed but did NOT enter the merge queue after enqueue (OMN-13214 armed-but-not-enqueued). Manual intervention required."
exit 1
116 changes: 116 additions & 0 deletions .github/workflows/canonical-inference-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# SPDX-FileCopyrightText: 2026 OmniNode.ai Inc.
# SPDX-License-Identifier: MIT
#
# OMN-13219: canonical-inference ratchet gate — BLOCKING MODE.
#
# Inference must go through the contract-driven routing authority (bus /
# inference-effect / Bifrost), never a shelled model CLI and never a
# hardcoded/env-resolved model or provider. This gate fails on:
# - model-cli-shellout: codex/claude/gemini/opencode passed to subprocess/exec
# or resolved via shutil.which
# - model-id-env-read: os.environ[*_MODEL] / os.environ.get(*_PROVIDER) reads
#
# Endpoint URLs and *_URL / *_ENDPOINT env reads are owned by the URL Authority
# Gate (OMN-12818); freestanding imperative IO generally is owned by the
# imperative-contract guard (OMN-12515/12540). This gate is the missing
# shell-out-for-inference layer.
#
# This is a RATCHET: existing violations are grandfathered by the sha256 baseline
# at src/omnibase_core/validation/baselines/canonical_inference_baseline.json.
# Any NEW violation (fingerprint absent from the baseline) fails the job and
# blocks merge. The baseline is burn-down only — a CI assertion rejects growth.
# The codex shell-out removal is tracked by OMN-13215.
#
# To permit a legitimate exception, annotate the line:
# # canonical-inference-ok: <concrete reason>
#
# This job is a REQUIRED STATUS CHECK. The job name "Canonical Inference Gate"
# must match branch protection rules.

name: Canonical Inference Gate

on:
pull_request:
types: [opened, edited, synchronize, reopened]
branches: [main, dev]
merge_group:

permissions:
contents: read

jobs:
canonical-inference-gate:
name: Canonical Inference Gate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Set up uv
uses: astral-sh/setup-uv@v7

- name: Install dependencies
run: uv sync --all-extras

- name: Run canonical-inference gate (full-repo, against frozen baseline)
run: |
uv run python -m omnibase_core.validation.validator_canonical_inference \
--all --repo omnibase_core --repo-root .

- name: Assert baseline did not grow (anti-gaming)
run: |
uv run python - <<'PY'
import json
import subprocess
import sys
from pathlib import Path

baseline = Path(
"src/omnibase_core/validation/baselines/canonical_inference_baseline.json"
)
head = json.loads(baseline.read_text())
head_repo = {
e["fingerprint"]
for e in head["violations"]
if e["repo"] == "omnibase_core"
}
# Compare against the baseline on the PR base branch.
base_ref = "origin/${{ github.base_ref || 'dev' }}"
try:
prior_text = subprocess.check_output(
["git", "show", f"{base_ref}:{baseline}"], text=True
)
prior = json.loads(prior_text)
prior_repo = {
e["fingerprint"]
for e in prior["violations"]
if e["repo"] == "omnibase_core"
}
except subprocess.CalledProcessError:
# Baseline file does not exist on the base branch — this is the
# initial introduction of the baseline for this repo. By definition
# nothing "grew" relative to a non-existent prior, so the check
# passes. Any new violations that sneak in via the first commit are
# caught by the gate step above.
print(
"CANONICAL-INFERENCE BASELINE OK (initial): "
f"baseline file is new on this PR ({len(head_repo)} grandfathered "
"fingerprints); no prior to compare against."
)
sys.exit(0)
grew = head_repo - prior_repo
if grew:
sys.stderr.write(
"CANONICAL-INFERENCE BASELINE GREW: "
f"{len(grew)} new fingerprint(s) added to the baseline. The "
"baseline is burn-down only — fix the violation or annotate with "
"# canonical-inference-ok:, never grow the baseline.\n"
)
sys.exit(1)
print(
"CANONICAL-INFERENCE BASELINE OK: "
f"omnibase_core subset {len(head_repo)} (<= prior {len(prior_repo)})."
)
PY
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1492,7 +1492,7 @@ jobs:
|| fromJSON(vars.OMNI_PUBLIC_PR_RUNS_ON_JSON || '["ubuntu-latest"]')
}}
steps:
- uses: OmniNode-ai/onex_change_control/.github/actions/validate-boundaries@644d04a17f32114d84321cfc1fedafff99aa4924 # main 2026-03-25
- uses: OmniNode-ai/onex_change_control/.github/actions/validate-boundaries@7b9ff965139d14416cf40e28d9ade254a08e6da1 # main
with:
checks: "boundary-parity"
warn-only: "false"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/url-authority-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Set up uv
uses: astral-sh/setup-uv@v5
uses: astral-sh/setup-uv@v7

- name: Install dependencies
run: uv sync --all-extras
Expand Down
19 changes: 19 additions & 0 deletions .onex_state/evidence/OMN-13132/deterministic-ir-evidence.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
=== DETERMINISTIC IR over >=2 REAL contracts (2 backend nodes + 1 UI component) ===
nodes: [('compliance_status_grid', 'component'), ('node.compliance.evidence', 'effect'), ('node.compliance.report.reducer', 'reducer')]
DETERMINISTIC (run1 JSON == run2 JSON): True

=== HASH MANIFEST ===
ui_component in-memory://compliance_status_grid
source_contract_sha256 = sha256:77b6cb763cc051afea434b84650c96958754794e727cba5b3bded1926f3f25e3
adapter_version_sha256 = sha256:aff0ebbd798f42325ea435bb681df7846916a47bdbbbd3268213b7389dc268f7
node src/omnibase_core/nodes/node_compliance_evidence_effect/contract.yaml
source_contract_sha256 = sha256:5ff3915eec87f68f60d5bee4b8436eea78d42aa0485bdfa333934c9a7c995766
adapter_version_sha256 = sha256:a8dfea4e9625322db32399f108d10a0920aea2c11f0f645491b8e99ed2f47134
node src/omnibase_core/nodes/node_compliance_report_reducer/contract.yaml
source_contract_sha256 = sha256:6bc01b5a506bed70de01db3738789f91ce4575fd96ece416307b2e81896c34c2
adapter_version_sha256 = sha256:a8dfea4e9625322db32399f108d10a0920aea2c11f0f645491b8e99ed2f47134

=== NO-OP ROUND-TRIP == ZERO SEMANTIC DIFF (via cli_contract_diff) ===
effect node : zero_diff=True total_changes=0
reducer node : zero_diff=True total_changes=0
ui component : zero_diff=True total_changes=0
50 changes: 50 additions & 0 deletions .onex_state/evidence/omn-13197-pr-body.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## OMN-13197 — Remove the `onex.runtime.intents` hardcoded topic + dead intent-publisher path

A-followup of **OMN-13188** (the **OMN-12803** "all topics/URLs from contracts only" family). Tracked separately per Q3 of the platform-debt plan so it does not block the A1–A5 sequence.

### Decision (per DoD: contract-source the topic OR confirm-and-delete the dead path)

**Confirm-and-delete the dead path.** The topic could **not** be contract-sourced:

- No contract anywhere declares `onex.runtime.intents` (`find ... contract.yaml -exec grep onex.runtime.intents` → no matches). The `contracts/intent_publisher.yaml` referenced in the mixin docstring was **never authored** (phantom reference).
- `ServiceTopicRegistry` (the contract-topic resolution mechanism) lives in `omnibase_infra`; `omnibase_core` cannot import it (repo layering: compat → core → spi → infra).

The mechanism was **architecturally dead from both sides** (verified in OMN-13188 / OMN-13192):

- `MixinIntentPublisher` — the **sole** publisher — had **ZERO production call sites** (never instantiated outside tests).
- `onex.runtime.intents` had **ZERO Kafka consumers**: `IntentExecutor` is a synchronous in-memory runtime service called from `ServiceDispatchResultApplier`, not a topic subscriber.

Per doctrine, a dead mechanism is debt to **delete**, not park / guard / migrate.

### Changed

**Removed**
- `TOPIC_EVENT_PUBLISH_INTENT` — a duplicate of the canonical `TOPIC_RUNTIME_INTENTS` (both `= topic_name(DOMAIN_RUNTIME, TOPIC_TYPE_INTENTS)`), existing only to feed the dead mixin — plus all re-exports (`constants/__init__.py`, `models/events/__init__.py`, `models/events/model_intent_events.py`).
- `MixinIntentPublisher` (`mixins/mixin_intent_publisher.py`) + its capability-mapping and metadata-census entries + mixins `__init__` export.
- `ModelIntentPublishResult` (`models/reducer/model_intent_publish_result.py`) — only consumer was the deleted mixin — + reducer `__init__` export.
- Dead tests + fixtures (`test_mixin_intent_publisher.py`, `test_intent_publisher_integration.py`, `test_model_intent_publish_result.py`, `fixture_intent_publisher.py`, `INTENT_PUBLISHER_TEST_SUMMARY.md`).
- Intent-publisher guide docs that documented the removed mechanism (`09_TESTING_INTENT_PUBLISHER.md`, `INTENT_PUBLISHER_TESTING_DOCUMENTATION.md`) + all inbound links (node-building tutorials, guides README, topic-taxonomy standard).

**Retained**
- `TOPIC_RUNTIME_INTENTS` — the canonical taxonomy derivation (`onex.runtime.intents`), part of the public `__all__`.
- `ModelEventPublishIntent` — has a live consumer in `utils/util_payload_migration.py`; docstring examples retargeted to `TOPIC_RUNTIME_INTENTS`.

**Added**
- `tests/unit/mixins/test_intent_publisher_removed.py` — regression guard asserting the dead symbols stay removed and the retained ones survive (TDD: red → green).

### dod_evidence
- `uv run ruff format src/ tests/` → 5567 files unchanged; `uv run ruff check --fix src/ tests/` → All checks passed.
- `uv run mypy src/ --strict` → 0 errors on all changed files. (6 pre-existing errors remain in untouched files: `contract_loader.py`, `cli_install.py`, `model_onex_container.py` — verified present on `dev` base, not introduced here.)
- Focused tests: `test_intent_publisher_removed.py` + `test_constants_topic_taxonomy.py` + `test_validator_hardcoded_topics.py` + `test_model_event_publish_intent.py` → **171 passed**. Full collection of `tests/unit/models` + reducer + events + intents → **1389 tests collected, 0 import errors**.
- `pre-commit run` on all changed files → **all hooks PASS** (markdown-link validation PASS after doc cleanup; deterministic-skill-routing PASS against the omniclaude `main` snapshot — the only fail was a sibling-clone-on-dev artifact unrelated to this diff).

### Receipt / OCC
- Evidence-Source: `2322a07e139c35bbd85f131395ce07ec44200713`
- Evidence-Ticket: OMN-13197

Paired OCC receipt PR: contracts/OMN-13197.yaml + drift/dod_receipts/OMN-13197/... (linked below).

Cross-reference: OMN-12803, OMN-13188, OMN-13192.


Paired OCC receipt PR: https://github.com/OmniNode-ai/onex_change_control/pull/2706
Loading
Loading