Skip to content

feat: add wire schema CI gate test generator [OMN-7371] - #136

Merged
jonahgabriel merged 5 commits into
mainfrom
jonah/omn-7371-ci-gate-generator
Apr 2, 2026
Merged

jonahgabriel merged 5 commits into
mainfrom
jonah/omn-7371-ci-gate-generator

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Apr 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Add wire_schema_test_generator.py that discovers wire schema contract YAMLs and generates parametrized pytest test cases
  • Each contract produces checks for: contract validity, duplicate fields, consumer field matching (Check 5), and model dump drift (Check 6)
  • Models not importable in the test environment are gracefully skipped
  • Includes pytest_params_from_contracts() for easy @pytest.mark.parametrize integration
  • Verified against existing routing_decision_v1.yaml — generates passing tests

Test plan

  • 11 unit tests pass (test_wire_schema_test_generator.py)
  • All 567 unit tests pass (no regressions)
  • Generator produces passing tests for routing_decision_v1.yaml
  • Generator produces correct test cases for synthetic fixtures

Depends on: #134, #135

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added wire schema compliance checking to validate schema contracts against producer and consumer models.
    • Introduced model dump drift detection to identify field mismatches between contracts and Pydantic models.
    • Enhanced compliance violation types to categorize schema and drift-related issues.
  • Improvements

    • Strengthened field validation for schema contract definitions.
    • Added testing utilities to generate and validate schema contract test cases.

@coderabbitai

coderabbitai Bot commented Apr 2, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@jonahgabriel has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 37 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 2 minutes and 37 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 698149f2-c882-4152-8ed1-60335b3c6cbf

📥 Commits

Reviewing files that changed from the base of the PR and between 6b6f9a0 and 7bc14a7.

📒 Files selected for processing (6)
  • src/onex_change_control/scanners/wire_schema_compliance.py
  • src/onex_change_control/testing/__init__.py
  • src/onex_change_control/testing/wire_schema_test_generator.py
  • tests/unit/models/test_model_wire_schema_contract.py
  • tests/unit/testing/__init__.py
  • tests/unit/testing/test_wire_schema_test_generator.py
📝 Walkthrough

Walkthrough

This PR introduces two new compliance violation scanners—Model Dump Drift and Wire Schema Mismatch—that validate consistency between Pydantic models and wire schema contracts. It extends the EnumComplianceViolation enum, tightens type constraints on wire schema fields, and adds comprehensive test infrastructure including a test case generator and unit test suites.

Changes

Cohort / File(s) Summary
Enum Extensions
src/onex_change_control/enums/enum_compliance_violation.py
Added WIRE_SCHEMA_MISMATCH and MODEL_DUMP_DRIFT enum members with corresponding docstrings for new compliance violation types.
Model Constraints
src/onex_change_control/models/model_wire_schema_contract.py
Narrowed ModelWireRenamedField.shim_status from unconstrained str to Literal["active", "retired"] for explicit value-level validation.
Scanner Implementations
src/onex_change_control/scanners/model_dump_drift.py, src/onex_change_control/scanners/wire_schema_compliance.py
Added two new scanner modules: model_dump_drift validates Pydantic model JSON schemas against contract field declarations and types; wire_schema_compliance discovers YAML contract files and checks producer/consumer field compatibility, including renamed field acceptance.
Testing Infrastructure
src/onex_change_control/testing/__init__.py, src/onex_change_control/testing/wire_schema_test_generator.py
Created new testing package with WireSchemaTestCase container and functions to generate pytest-ready test cases from discovered contracts, supporting dynamic consumer model introspection and test case aggregation.
Scanner Test Suites
tests/unit/scanners/test_model_dump_drift.py, tests/unit/scanners/test_wire_schema_compliance.py, tests/unit/scanners/__init__.py
Added comprehensive unit tests for both scanners covering drift/mismatch scenarios, renamed field handling, nullable field representation, and edge cases; added package headers.
Test Generator Suite
tests/unit/testing/test_wire_schema_test_generator.py
Added tests validating test case generation logic, contract discovery, pytest parametrization, and compatibility with local contract repositories.
Existing Test Updates
tests/unit/models/test_model_wire_schema_contract.py, tests/unit/test_compliance_models.py
Updated routing decision test with environment-adaptive path resolution; updated enum cardinality assertion from 8 to 10 members.

Sequence Diagram(s)

sequenceDiagram
    participant Contract as Wire Schema<br/>Contract
    participant Generator as Test Generator
    participant Discoverer as Contract<br/>Discoverer
    participant Importer as Model<br/>Importer
    participant Schema as JSON Schema

    Generator->>Discoverer: discover_wire_schema_contracts(search_dirs)
    Discoverer->>Contract: parse YAML files
    Discoverer-->>Generator: list[Contract]
    
    Generator->>Contract: extract contract fields
    Generator->>Importer: import consumer model FQN
    Importer->>Schema: model_json_schema()
    Schema-->>Importer: JSON schema dict
    Importer-->>Generator: schema properties
    
    Generator->>Generator: generate baseline cases<br/>(contract_valid,<br/>no_duplicate_fields)
    Generator->>Generator: check consumer fields<br/>against contract
    Generator->>Generator: check model dump drift
    Generator-->>Generator: list[WireSchemaTestCase]
Loading
sequenceDiagram
    participant Scanner as Wire Schema<br/>Scanner
    participant Discoverer as Contract<br/>Discoverer
    participant Contract as ModelWireSchemaContract
    participant ProducerModel as Producer<br/>Model
    participant ConsumerModel as Consumer<br/>Model
    participant Checker as Mismatch<br/>Checker

    Scanner->>Discoverer: discover_wire_schema_contracts(search_dirs)
    Discoverer-->>Scanner: list[(path, contract)]
    
    loop For Each Contract
        Scanner->>Contract: extract required/optional fields
        Scanner->>ProducerModel: import FQN & extract fields
        ProducerModel-->>Scanner: field set
        Scanner->>ConsumerModel: import FQN & extract fields
        ConsumerModel-->>Scanner: field set
        
        Scanner->>Checker: check_wire_schema_mismatch(contract, ...)
        Checker->>Checker: compare required fields<br/>check renamed aliases<br/>validate undeclared fields
        Checker-->>Scanner: list[ModelWireSchemaViolation]
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 Hop, hop, schema compliance checks!
Two new scanners catch the wrecks,
Model drift and wire mismatch found,
Validation logic, safe and sound!
Test cases spring from contracts true,
This rabbit's work is quite through-bred, too! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add wire schema CI gate test generator [OMN-7371]' is highly specific and directly describes the main change—adding a test generator for wire schema CI gates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jonah/omn-7371-ci-gate-generator

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread tests/unit/models/test_model_wire_schema_contract.py Fixed
Comment thread src/onex_change_control/scanners/model_dump_drift.py Fixed
Comment thread src/onex_change_control/testing/wire_schema_test_generator.py Fixed
Comment thread src/onex_change_control/testing/wire_schema_test_generator.py Fixed
Comment thread tests/unit/models/test_model_wire_schema_contract.py Fixed
Comment thread tests/unit/scanners/test_model_dump_drift.py Fixed
Comment thread tests/unit/scanners/test_model_dump_drift.py Fixed
Comment thread tests/unit/scanners/test_wire_schema_compliance.py Fixed
Comment thread tests/unit/scanners/test_wire_schema_compliance.py Fixed
Comment thread tests/unit/testing/test_wire_schema_test_generator.py Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (11)
src/onex_change_control/scanners/model_dump_drift.py (2)

19-25: Remove unused import and empty TYPE_CHECKING block.

EnumWireFieldType is imported but never used. The TYPE_CHECKING block contains only pass and serves no purpose.

♻️ Proposed fix
 from onex_change_control.models.model_wire_schema_contract import (
-    EnumWireFieldType,
     ModelWireSchemaContract,
 )
-
-if TYPE_CHECKING:
-    pass
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/scanners/model_dump_drift.py` around lines 19 - 25,
Remove the unused import EnumWireFieldType from the import tuple in
model_dump_drift.py and delete the empty TYPE_CHECKING block; update the import
statement to only import ModelWireSchemaContract and remove the entire "if
TYPE_CHECKING: pass" section so there are no dead imports or no-op type-checking
blocks remaining.

41-44: Consider extracting _PYDANTIC_INTERNAL to a shared constant.

This exact set is duplicated in src/onex_change_control/scanners/wire_schema_compliance.py (line 204). Consider defining it once in a shared location to maintain consistency if the set of internal fields changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/scanners/model_dump_drift.py` around lines 41 - 44,
The _PYDANTIC_INTERNAL frozenset is duplicated; extract it into a single shared
constant (e.g., PYDANTIC_INTERNAL_FIELDS) in a common module and replace the
local definitions in model_dump_drift.py and wire_schema_compliance.py with
imports of that symbol; update any references to use the new constant name, run
tests/import checks, and ensure you export the constant from the shared module
so both modules (previously declaring _PYDANTIC_INTERNAL) import the single
source of truth.
tests/unit/models/test_model_wire_schema_contract.py (1)

148-170: Consider using pytest.raises(ValidationError) instead of broad Exception.

Catching Exception is overly broad and could mask unexpected errors. Since load_wire_schema_contract uses Pydantic's model_validate(), missing required fields raise pydantic.ValidationError.

♻️ Proposed fix
+from pydantic import ValidationError
+
 class TestModelWireSchemaContractInvalid:
     """Invalid contract rejection."""

     def test_missing_required_fields_section(self) -> None:
         data = _minimal_contract()
         del data["required_fields"]
-        with pytest.raises(Exception):
+        with pytest.raises(ValidationError):
             load_wire_schema_contract(data)

     def test_missing_topic(self) -> None:
         data = _minimal_contract()
         del data["topic"]
-        with pytest.raises(Exception):
+        with pytest.raises(ValidationError):
             load_wire_schema_contract(data)

     def test_missing_producer(self) -> None:
         data = _minimal_contract()
         del data["producer"]
-        with pytest.raises(Exception):
+        with pytest.raises(ValidationError):
             load_wire_schema_contract(data)

     def test_missing_consumer(self) -> None:
         data = _minimal_contract()
         del data["consumer"]
-        with pytest.raises(Exception):
+        with pytest.raises(ValidationError):
             load_wire_schema_contract(data)

Also update line 210:

     def test_invalid_field_type(self) -> None:
         data = _minimal_contract(
             required_fields=[
                 {"name": "id", "type": "invalid_type"},
             ]
         )
-        with pytest.raises(Exception):
+        with pytest.raises(ValidationError):
             load_wire_schema_contract(data)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/models/test_model_wire_schema_contract.py` around lines 148 - 170,
Replace the broad exception assertions in the tests so they specifically expect
Pydantic validation failures: change the pytest.raises(Exception) calls to
pytest.raises(pydantic.ValidationError) in the
test_missing_required_fields_section, test_missing_topic, test_missing_producer,
and test_missing_consumer tests that call load_wire_schema_contract; also scan
the same test file for other uses of pytest.raises(Exception) (e.g., the other
assertion noted around the previous change) and update them to
pytest.raises(pydantic.ValidationError) so tests assert the correct error type
from model_validate().
tests/unit/scanners/test_wire_schema_compliance.py (1)

13-23: Remove unused import ModelWireSchemaViolation.

Static analysis correctly identifies that ModelWireSchemaViolation is imported but never used. The tests access violation attributes directly without type annotations. Note that pytest is actually used implicitly via the tmp_path fixture.

🧹 Proposed fix
 import pytest
 import yaml
 
 from onex_change_control.models.model_wire_schema_contract import (
     load_wire_schema_contract,
 )
 from onex_change_control.scanners.wire_schema_compliance import (
-    ModelWireSchemaViolation,
     check_wire_schema_mismatch,
     discover_wire_schema_contracts,
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/scanners/test_wire_schema_compliance.py` around lines 13 - 23,
Remove the unused import ModelWireSchemaViolation from the import list in the
test module; update the import statement that currently brings in
ModelWireSchemaViolation, check_wire_schema_mismatch, and
discover_wire_schema_contracts so it only imports check_wire_schema_mismatch and
discover_wire_schema_contracts, and run tests to ensure no references to
ModelWireSchemaViolation remain (tests already access violation attributes
directly).
src/onex_change_control/models/model_wire_schema_contract.py (1)

76-88: Consider using an enum for shim_status.

The shim_status field accepts any string but per the docstring should only be "active" or "retired". Using a Literal["active", "retired"] or a dedicated enum would provide compile-time validation.

🧹 Proposed improvement
+from typing import Literal
+
 class ModelWireRenamedField(BaseModel):
     """A renamed field tracking an active or retired shim."""

     model_config = ConfigDict(frozen=True, extra="forbid")

     producer_name: str = Field(..., description="Name emitted by the producer")
     canonical_name: str = Field(..., description="Canonical name in the contract")
-    shim_status: str = Field(
-        ..., description="active or retired"
-    )
+    shim_status: Literal["active", "retired"] = Field(
+        ..., description="Shim status"
+    )
     retirement_ticket: str = Field(
         default="", description="Ticket tracking shim retirement"
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/models/model_wire_schema_contract.py` around lines 76
- 88, The shim_status field on ModelWireRenamedField currently accepts any
string; restrict it to the allowed values by changing its type to a
Literal["active", "retired"] or an Enum (e.g., ShimStatus with ACTIVE/RETIRED)
and update the Field declaration accordingly; import typing.Literal (or the Enum
class) and ensure model_config still enforces validation so only "active" or
"retired" are accepted at runtime.
src/onex_change_control/scanners/wire_schema_compliance.py (1)

202-218: Move _PYDANTIC_INTERNAL to module-level constant.

The set _PYDANTIC_INTERNAL is recreated on every call to _check_side. Moving it to module level avoids repeated allocation and makes it easier to maintain.

♻️ Proposed fix

At module level (e.g., after line 30):

# Internal Pydantic fields to exclude from contract validation
_PYDANTIC_INTERNAL = frozenset({"model_config", "model_fields", "model_computed_fields"})

Then in _check_side:

     # Check: model field not in contract (undeclared emission/consumption)
-    # Exclude internal Pydantic fields
-    _PYDANTIC_INTERNAL = {"model_config", "model_fields", "model_computed_fields"}
     for field_name in sorted(model_fields - _PYDANTIC_INTERNAL):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/scanners/wire_schema_compliance.py` around lines 202
- 218, The _PYDANTIC_INTERNAL set is being recreated each call inside
_check_side; move it to a module-level constant to avoid repeated allocation and
centralize maintenance by defining _PYDANTIC_INTERNAL =
frozenset({"model_config","model_fields","model_computed_fields"}) at module
scope (near other constants) and then remove the local definition inside
_check_side so the loop that iterates model_fields - _PYDANTIC_INTERNAL uses the
module constant; ensure references to _PYDANTIC_INTERNAL and behavior in the
loop (including the producer rename_producer_names logic and appended
ModelWireSchemaViolation) remain unchanged.
src/onex_change_control/testing/wire_schema_test_generator.py (4)

62-69: Consider logging import failures for debugging.

The bare except Exception silently swallows all errors. While graceful degradation is appropriate for non-importable models, logging at DEBUG level would aid troubleshooting when models fail to resolve unexpectedly.

🔧 Proposed improvement
 def _resolve_model_json_schema(module_path: str, class_name: str) -> dict[str, Any] | None:
     """Import a model and return its JSON schema, or None if not resolvable."""
     try:
         mod = importlib.import_module(module_path)
         cls = getattr(mod, class_name)
         return cls.model_json_schema()
-    except Exception:
+    except Exception as e:
+        logger.debug("Could not resolve model %s.%s: %s", module_path, class_name, e)
         return None
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/testing/wire_schema_test_generator.py` around lines
62 - 69, The helper _resolve_model_json_schema currently swallows all import
errors; update it to log failures at DEBUG so unexpected resolution problems are
visible during troubleshooting: catch Exception as e, obtain a logger (e.g.,
logging.getLogger(__name__) or a module-level logger), and call logger.debug
with a clear message including module_path, class_name and the exception
information (or use logger.exception/stack_info if available) before returning
None; ensure _resolve_model_json_schema remains returning None on failure.

23-28: Remove unused imports yaml and load_wire_schema_contract.

Static analysis correctly identifies these as unused. The module uses discover_wire_schema_contracts which handles YAML loading internally.

🧹 Proposed fix
-import yaml
-
 from onex_change_control.models.model_wire_schema_contract import (
     ModelWireSchemaContract,
-    load_wire_schema_contract,
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/testing/wire_schema_test_generator.py` around lines
23 - 28, Remove the unused imports from the top of the module: delete the import
of yaml and the import of load_wire_schema_contract from
ModelWireSchemaContract's import block; keep only ModelWireSchemaContract (or
other actually used symbols) since the code uses discover_wire_schema_contracts
which handles YAML loading internally.

72-85: Remove unused parameter model_name.

The model_name parameter is never used in the function body. If it was intended for future use, consider removing it until needed to avoid confusion.

🧹 Proposed fix
-def _infer_module_from_file(file_path: str, model_name: str) -> str | None:
+def _infer_module_from_file(file_path: str) -> str | None:
     """Infer a Python module path from a contract's file path.

     E.g., "src/omnibase_infra/models/model_foo.py" -> "omnibase_infra.models.model_foo"
     """

And update the call site at line 137-138:

     consumer_module = _infer_module_from_file(
-        contract.consumer.file, contract.consumer.model
+        contract.consumer.file
     )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/testing/wire_schema_test_generator.py` around lines
72 - 85, The function _infer_module_from_file has an unused parameter
model_name; remove that parameter from its signature and any internal references
so the function becomes _infer_module_from_file(file_path: str) -> str | None,
and then update all call sites that currently pass a model_name (calls to
_infer_module_from_file) to only pass the file_path argument. Ensure
imports/type hints and any tests referencing the old signature are updated
accordingly.

195-215: Add type parameter to search_dirs for clarity.

The search_dirs parameter is typed as bare list without a type parameter. For consistency with discover_wire_schema_contracts and better IDE support, specify the element type.

🧹 Proposed fix
+from pathlib import Path
+
 def generate_all_test_cases(
-    search_dirs: list,
+    search_dirs: list[Path],
 ) -> list[WireSchemaTestCase]:

Similarly for pytest_params_from_contracts:

 def pytest_params_from_contracts(
-    search_dirs: list,
+    search_dirs: list[Path],
 ) -> list[tuple[str, str, bool, str]]:

Note: Move the Path import to the top-level imports and remove the in-function import on line 206.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/testing/wire_schema_test_generator.py` around lines
195 - 215, The parameter search_dirs in generate_all_test_cases is typed as a
bare list; update its annotation to list[Path | str] (or list[Path] if you only
accept Paths) to match discover_wire_schema_contracts and improve IDE support,
and do the same for pytest_params_from_contracts if it also uses a bare list;
also move the Path import out of the function into the module-level imports and
remove the in-function import to avoid repeated imports. Use the function name
generate_all_test_cases, the parameter search_dirs, and reference
discover_wire_schema_contracts and pytest_params_from_contracts when making
these edits.
tests/unit/testing/test_wire_schema_test_generator.py (1)

13-18: Remove unused import WireSchemaTestCase.

Static analysis correctly identifies that WireSchemaTestCase is imported but not directly referenced in type annotations or assertions. The tests work with the returned instances via attribute access.

🧹 Proposed fix
 from onex_change_control.testing.wire_schema_test_generator import (
-    WireSchemaTestCase,
     generate_all_test_cases,
     generate_test_cases_for_contract,
     pytest_params_from_contracts,
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/testing/test_wire_schema_test_generator.py` around lines 13 - 18,
The import list includes an unused symbol WireSchemaTestCase; remove
WireSchemaTestCase from the from-import tuple in
test_wire_schema_test_generator.py so only used symbols
(generate_all_test_cases, generate_test_cases_for_contract,
pytest_params_from_contracts) are imported, ensuring the import statement
remains syntactically correct and tests continue to use returned instances via
attribute access.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/unit/models/test_model_wire_schema_contract.py`:
- Around line 223-250: The test test_parse_routing_decision_contract uses a
hardcoded local path in yaml_path which will not exist in CI; instead update the
test to load the routing_decision_v1.yaml from a project-relative test fixture
or resource (e.g., place the YAML into the tests/fixtures or tests/data
directory) and open it using a relative path or a pytest fixture/env var to
locate the file, then call load_wire_schema_contract(data) unchanged and assert
on contract as before; ensure the test fails fast if the fixture is missing (do
not silently pytest.skip) so CI verifies the contract parsing behavior.

In `@tests/unit/testing/test_wire_schema_test_generator.py`:
- Around line 137-155: The test
TestRoutingDecisionV1Compatibility::test_generates_passing_tests_for_routing_decision
contains a hardcoded developer path; replace this with a configurable approach:
remove the absolute Path and instead read a path from an environment variable or
pytest fixture (e.g., use an env var like OMNIBASE_INFRA_PATH or a fixture that
yields a Path), fall back to pytest.skip when not provided, and ensure you still
call generate_all_test_cases([path]) and filter routing_cases as before;
alternatively delete the test and rely on integration tests if you prefer not to
support an optional local dependency.

---

Nitpick comments:
In `@src/onex_change_control/models/model_wire_schema_contract.py`:
- Around line 76-88: The shim_status field on ModelWireRenamedField currently
accepts any string; restrict it to the allowed values by changing its type to a
Literal["active", "retired"] or an Enum (e.g., ShimStatus with ACTIVE/RETIRED)
and update the Field declaration accordingly; import typing.Literal (or the Enum
class) and ensure model_config still enforces validation so only "active" or
"retired" are accepted at runtime.

In `@src/onex_change_control/scanners/model_dump_drift.py`:
- Around line 19-25: Remove the unused import EnumWireFieldType from the import
tuple in model_dump_drift.py and delete the empty TYPE_CHECKING block; update
the import statement to only import ModelWireSchemaContract and remove the
entire "if TYPE_CHECKING: pass" section so there are no dead imports or no-op
type-checking blocks remaining.
- Around line 41-44: The _PYDANTIC_INTERNAL frozenset is duplicated; extract it
into a single shared constant (e.g., PYDANTIC_INTERNAL_FIELDS) in a common
module and replace the local definitions in model_dump_drift.py and
wire_schema_compliance.py with imports of that symbol; update any references to
use the new constant name, run tests/import checks, and ensure you export the
constant from the shared module so both modules (previously declaring
_PYDANTIC_INTERNAL) import the single source of truth.

In `@src/onex_change_control/scanners/wire_schema_compliance.py`:
- Around line 202-218: The _PYDANTIC_INTERNAL set is being recreated each call
inside _check_side; move it to a module-level constant to avoid repeated
allocation and centralize maintenance by defining _PYDANTIC_INTERNAL =
frozenset({"model_config","model_fields","model_computed_fields"}) at module
scope (near other constants) and then remove the local definition inside
_check_side so the loop that iterates model_fields - _PYDANTIC_INTERNAL uses the
module constant; ensure references to _PYDANTIC_INTERNAL and behavior in the
loop (including the producer rename_producer_names logic and appended
ModelWireSchemaViolation) remain unchanged.

In `@src/onex_change_control/testing/wire_schema_test_generator.py`:
- Around line 62-69: The helper _resolve_model_json_schema currently swallows
all import errors; update it to log failures at DEBUG so unexpected resolution
problems are visible during troubleshooting: catch Exception as e, obtain a
logger (e.g., logging.getLogger(__name__) or a module-level logger), and call
logger.debug with a clear message including module_path, class_name and the
exception information (or use logger.exception/stack_info if available) before
returning None; ensure _resolve_model_json_schema remains returning None on
failure.
- Around line 23-28: Remove the unused imports from the top of the module:
delete the import of yaml and the import of load_wire_schema_contract from
ModelWireSchemaContract's import block; keep only ModelWireSchemaContract (or
other actually used symbols) since the code uses discover_wire_schema_contracts
which handles YAML loading internally.
- Around line 72-85: The function _infer_module_from_file has an unused
parameter model_name; remove that parameter from its signature and any internal
references so the function becomes _infer_module_from_file(file_path: str) ->
str | None, and then update all call sites that currently pass a model_name
(calls to _infer_module_from_file) to only pass the file_path argument. Ensure
imports/type hints and any tests referencing the old signature are updated
accordingly.
- Around line 195-215: The parameter search_dirs in generate_all_test_cases is
typed as a bare list; update its annotation to list[Path | str] (or list[Path]
if you only accept Paths) to match discover_wire_schema_contracts and improve
IDE support, and do the same for pytest_params_from_contracts if it also uses a
bare list; also move the Path import out of the function into the module-level
imports and remove the in-function import to avoid repeated imports. Use the
function name generate_all_test_cases, the parameter search_dirs, and reference
discover_wire_schema_contracts and pytest_params_from_contracts when making
these edits.

In `@tests/unit/models/test_model_wire_schema_contract.py`:
- Around line 148-170: Replace the broad exception assertions in the tests so
they specifically expect Pydantic validation failures: change the
pytest.raises(Exception) calls to pytest.raises(pydantic.ValidationError) in the
test_missing_required_fields_section, test_missing_topic, test_missing_producer,
and test_missing_consumer tests that call load_wire_schema_contract; also scan
the same test file for other uses of pytest.raises(Exception) (e.g., the other
assertion noted around the previous change) and update them to
pytest.raises(pydantic.ValidationError) so tests assert the correct error type
from model_validate().

In `@tests/unit/scanners/test_wire_schema_compliance.py`:
- Around line 13-23: Remove the unused import ModelWireSchemaViolation from the
import list in the test module; update the import statement that currently
brings in ModelWireSchemaViolation, check_wire_schema_mismatch, and
discover_wire_schema_contracts so it only imports check_wire_schema_mismatch and
discover_wire_schema_contracts, and run tests to ensure no references to
ModelWireSchemaViolation remain (tests already access violation attributes
directly).

In `@tests/unit/testing/test_wire_schema_test_generator.py`:
- Around line 13-18: The import list includes an unused symbol
WireSchemaTestCase; remove WireSchemaTestCase from the from-import tuple in
test_wire_schema_test_generator.py so only used symbols
(generate_all_test_cases, generate_test_cases_for_contract,
pytest_params_from_contracts) are imported, ensuring the import statement
remains syntactically correct and tests continue to use returned instances via
attribute access.
🪄 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: d3a376e7-b650-4f9c-9875-d50bbfa2ea22

📥 Commits

Reviewing files that changed from the base of the PR and between 09cc8ba and 977df53.

📒 Files selected for processing (14)
  • docs/wire-schema-contract-spec.md
  • src/onex_change_control/enums/enum_compliance_violation.py
  • src/onex_change_control/models/__init__.py
  • src/onex_change_control/models/model_wire_schema_contract.py
  • src/onex_change_control/scanners/model_dump_drift.py
  • src/onex_change_control/scanners/wire_schema_compliance.py
  • src/onex_change_control/testing/__init__.py
  • src/onex_change_control/testing/wire_schema_test_generator.py
  • tests/unit/models/test_model_wire_schema_contract.py
  • tests/unit/scanners/test_model_dump_drift.py
  • tests/unit/scanners/test_wire_schema_compliance.py
  • tests/unit/test_compliance_models.py
  • tests/unit/testing/__init__.py
  • tests/unit/testing/test_wire_schema_test_generator.py

Comment thread tests/unit/models/test_model_wire_schema_contract.py
Comment thread tests/unit/testing/test_wire_schema_test_generator.py
@jonahgabriel
jonahgabriel enabled auto-merge April 2, 2026 21:53
@jonahgabriel
jonahgabriel force-pushed the jonah/omn-7371-ci-gate-generator branch from 977df53 to c86de13 Compare April 2, 2026 21:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/onex_change_control/scanners/model_dump_drift.py (1)

40-42: Consider adding model_extra to internal fields list.

The _PYDANTIC_INTERNAL set excludes common Pydantic internals, but model_extra may also appear in JSON schemas when extra="allow" is configured. This is a minor edge case since the wire schema contracts use extra="forbid" or extra="ignore".

♻️ Optional: expand internal fields list
 _PYDANTIC_INTERNAL: frozenset[str] = frozenset(
-    {"model_config", "model_fields", "model_computed_fields"}
+    {"model_config", "model_fields", "model_computed_fields", "model_extra"}
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/scanners/model_dump_drift.py` around lines 40 - 42,
The _PYDANTIC_INTERNAL frozenset currently lists "model_config", "model_fields",
and "model_computed_fields" but should also include "model_extra" to cover cases
where Pydantic emits that key (e.g., when extra="allow"); update the
_PYDANTIC_INTERNAL definition to add "model_extra" so functions that filter or
compare JSON schemas (referencing _PYDANTIC_INTERNAL) will treat it as an
internal field.
src/onex_change_control/testing/wire_schema_test_generator.py (1)

211-213: Defensive type coercion is acceptable but consider tightening the type.

Line 212 converts elements to Path even though search_dirs is typed as list[Path]. While this defensive approach handles misuse, it may mask type errors. Consider either removing the conversion or updating the type hint to list[Path | str] for clarity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onex_change_control/testing/wire_schema_test_generator.py` around lines
211 - 213, The code coerces elements of search_dirs into Path objects before
calling discover_wire_schema_contracts, but search_dirs is annotated as
list[Path]; to fix, either remove the defensive conversion and pass search_dirs
directly to discover_wire_schema_contracts (if search_dirs should always be
Path), or update the type annotation on search_dirs to list[Path | str] and keep
the conversion to Path for safety; update the function signature or its callers
accordingly and ensure references to discover_wire_schema_contracts,
search_dirs, and Path are adjusted to match the chosen approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/onex_change_control/testing/wire_schema_test_generator.py`:
- Around line 184-196: When the consumer model isn't importable the code
currently appends only a WireSchemaTestCase with check_name
"consumer_fields_match" and skips adding "consumer_model_drift", causing
inconsistent counts; update the else branch that constructs the
WireSchemaTestCase (look for WireSchemaTestCase usage with contract_path,
contract and the message referencing contract.consumer.model) to also append a
second WireSchemaTestCase for check_name "consumer_model_drift" with passed=True
and details indicating the same skipped reason (e.g. "Skipped: consumer model
{contract.consumer.model} not importable"), preserving the same
contract_path/contract context.

In `@tests/unit/testing/test_wire_schema_test_generator.py`:
- Around line 23-24: Update _make_contract to use typed generics: import Any and
Dict from typing, change the signature to def _make_contract(**overrides: Any)
-> Dict[str, Any], and change the local base variable annotation from base: dict
= to base: Dict[str, Any] = so no bare dict is used; also update any other
occurrences in that function that use unparameterized dict to use Dict[str,
Any].

---

Nitpick comments:
In `@src/onex_change_control/scanners/model_dump_drift.py`:
- Around line 40-42: The _PYDANTIC_INTERNAL frozenset currently lists
"model_config", "model_fields", and "model_computed_fields" but should also
include "model_extra" to cover cases where Pydantic emits that key (e.g., when
extra="allow"); update the _PYDANTIC_INTERNAL definition to add "model_extra" so
functions that filter or compare JSON schemas (referencing _PYDANTIC_INTERNAL)
will treat it as an internal field.

In `@src/onex_change_control/testing/wire_schema_test_generator.py`:
- Around line 211-213: The code coerces elements of search_dirs into Path
objects before calling discover_wire_schema_contracts, but search_dirs is
annotated as list[Path]; to fix, either remove the defensive conversion and pass
search_dirs directly to discover_wire_schema_contracts (if search_dirs should
always be Path), or update the type annotation on search_dirs to list[Path |
str] and keep the conversion to Path for safety; update the function signature
or its callers accordingly and ensure references to
discover_wire_schema_contracts, search_dirs, and Path are adjusted to match the
chosen approach.
🪄 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: 0c67e3ac-38cc-4ab8-a528-854268f7894f

📥 Commits

Reviewing files that changed from the base of the PR and between 977df53 and 6b6f9a0.

📒 Files selected for processing (13)
  • src/onex_change_control/enums/enum_compliance_violation.py
  • src/onex_change_control/models/model_wire_schema_contract.py
  • src/onex_change_control/scanners/model_dump_drift.py
  • src/onex_change_control/scanners/wire_schema_compliance.py
  • src/onex_change_control/testing/__init__.py
  • src/onex_change_control/testing/wire_schema_test_generator.py
  • tests/unit/models/test_model_wire_schema_contract.py
  • tests/unit/scanners/__init__.py
  • tests/unit/scanners/test_model_dump_drift.py
  • tests/unit/scanners/test_wire_schema_compliance.py
  • tests/unit/test_compliance_models.py
  • tests/unit/testing/__init__.py
  • tests/unit/testing/test_wire_schema_test_generator.py
✅ Files skipped from review due to trivial changes (5)
  • tests/unit/scanners/init.py
  • src/onex_change_control/testing/init.py
  • src/onex_change_control/enums/enum_compliance_violation.py
  • tests/unit/scanners/test_model_dump_drift.py
  • tests/unit/scanners/test_wire_schema_compliance.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/unit/test_compliance_models.py
  • src/onex_change_control/scanners/wire_schema_compliance.py

Comment thread src/onex_change_control/testing/wire_schema_test_generator.py
Comment thread tests/unit/testing/test_wire_schema_test_generator.py Outdated
jonahgabriel and others added 3 commits April 2, 2026 18:37
Create wire_schema_test_generator.py that discovers wire schema contract
YAMLs and generates parametrized pytest test cases. Each contract produces
checks for: contract validity, duplicate fields, consumer field matching
(Check 5), and model dump drift (Check 6). Models that aren't importable
in the test env are gracefully skipped.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…or [OMN-7371]

- Add tests/unit/scanners/__init__.py (INP001 namespace package fix)
- Remove unused imports: EnumWireFieldType, yaml, load_wire_schema_contract,
  ModelWireSchemaContract, WireSchemaTestCase, pytest, ModelDumpDriftViolation,
  ModelWireSchemaViolation (F401 across scanner/test files)
- Replace blind except Exception with specific exceptions (BLE001)
- Fix __slots__ ordering to natural sort order (RUF023)
- Move ModelWireSchemaContract to TYPE_CHECKING block (TC001)
- Remove unused model_name param from _infer_module_from_file (ARG001)
- Move Path import to top level, remove in-function import (PLC0415)
- Make passed a keyword-only argument in WireSchemaTestCase (FBT001)
- Fix E501 line length violations
- shim_status constrained to Literal["active", "retired"]
- Handle multiple active producer aliases per canonical field in _check_side
- scan_wire_schema_compliance derives FQNs from contract file paths
- Fix hardcoded /Users/jonah path in tests: try multiple candidate dirs
- Fix potentially uninitialized data variable in routing_decision test

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ype params [OMN-7371]

- Add SPDX header to tests/unit/testing/__init__.py (pre-commit SPDX gate)
- Add dict[str, Any] type parameters to _make_contract in test generator
  (mypy type-arg requirement)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jonahgabriel
jonahgabriel force-pushed the jonah/omn-7371-ci-gate-generator branch from 30856fe to 845a599 Compare April 2, 2026 22:38
… [OMN-7371]

- Replace hardcoded developer paths with OMNI_HOME env var + fallback candidates
- Add missing consumer_model_drift skip case when model not importable
- Addresses CodeRabbit Major + Minor review comments
@jonahgabriel
jonahgabriel added this pull request to the merge queue Apr 2, 2026
Merged via the queue into main with commit 3d7fae9 Apr 2, 2026
19 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-7371-ci-gate-generator branch April 2, 2026 22:47
jonahgabriel added a commit that referenced this pull request May 26, 2026
…elf-extending-agent fixes (#1689)

Adds OCC contracts and receipt evidence for two onex-self-extending-agent tickets:

- OMN-11827: max_tokens truncation fix (PRs #136 merged, #138 open)
- OMN-12124: clean ADK auth error handling (PR #137 open)

These contracts unblock the receipt gate on those PRs — both reference OCC#<this PR>.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants