Repository navigation
M1: Implement Pydantic schema models v1 (Day Close + Ticket Contract) - #4
Conversation
Implements OMN-962 - Complete schema models for drift control artifacts. Deliverables: - All enum files following naming conventions (Enum*/enum_*.py): * EnumDriftCategory - drift categories * EnumEvidenceKind - evidence types * EnumInterfaceSurface - interface surfaces * EnumInvariantStatus - invariant check status * EnumPRState - PR states - All model files following naming conventions (Model*/model_*.py): * ModelDayClose + 7 supporting models * ModelTicketContract + 2 supporting models - SemVer validation for schema_version fields - Schema purity: no runtime/infra coupling - 97% test coverage (12 tests, all passing) - Models validate existing drift reports Acceptance criteria met: ✅ Unit tests cover core structural validation ✅ Models are schema-pure (no runtime/infra coupling) ✅ Naming conventions aligned with omnibase_core ✅ schema_version validated as SemVer Refs: OMN-962
WalkthroughAdds new Pydantic schemas for day-close reports and ticket contracts, five enums, shared validation patterns, package-level re-exports, a revised day-close YAML, and a comprehensive test suite covering parsing, validation, security limits, and (de)serialization. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (5)
tests/test_yaml_parsing.py (1)
11-29: Consider adding file existence guard or using pytest fixture.The test assumes
drift/day_close/2025-12-19.yamlexists. If this file is modified or removed in future PRs, this test will fail. Consider usingpytest.mark.skipifwith a path existence check, or defining the test data inline liketest_parse_ticket_contract_templatedoes.🔎 Optional: Add existence check
+import pytest + +YAML_PATH = Path(__file__).parent.parent / "drift" / "day_close" / "2025-12-19.yaml" + +@pytest.mark.skipif(not YAML_PATH.exists(), reason="YAML fixture not present") def test_parse_existing_day_close_yaml() -> None: """Test parsing existing day_close.yaml file.""" - yaml_path = Path(__file__).parent.parent / "drift" / "day_close" / "2025-12-19.yaml" + yaml_path = YAML_PATH with yaml_path.open() as f:src/onex_change_control/models/model_ticket_contract.py (2)
14-15: Consider extracting shared SemVer pattern.This pattern is duplicated in
model_day_close.py. Consider extracting to a shared module (e.g.,_validators.pyor_patterns.py) to maintain DRY and ensure consistency.
62-68: RedundantField()definition.The
schema_versionfield uses bothAnnotated[str, Field(...)]and a separate= Field(...)assignment, which duplicates the description. Use one approach:🔎 Proposed fix using Annotated only
- schema_version: Annotated[ - str, - Field( - ..., - description="Schema version (SemVer format, e.g., '1.0.0')", - ), - ] = Field(..., description="Schema version (SemVer format)") + schema_version: Annotated[ + str, + Field(..., description="Schema version (SemVer format, e.g., '1.0.0')"), + ]src/onex_change_control/models/model_day_close.py (2)
94-100: RedundantFielddefinition forschema_version.The field is defined twice - once in the
Annotatedwrapper and again as the default value. This duplication is unnecessary and could cause confusion about which description takes precedence.🔎 Suggested fix
- schema_version: Annotated[ - str, - Field( - ..., - description="Schema version (SemVer format, e.g., '1.0.0')", - ), - ] = Field(..., description="Schema version (SemVer format)") + schema_version: str = Field( + ..., description="Schema version (SemVer format, e.g., '1.0.0')" + )
139-147: Move date pattern to module level for consistency and performance.The
date_patternregex is compiled inside the validator function on every call, unlike_SEMVER_PATTERNwhich is compiled once at module level.🔎 Suggested fix
Add at module level (after line 16):
_DATE_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}$")Then update the validator:
@field_validator("date") @classmethod def validate_date(cls, v: str) -> str: """Validate date is ISO format (YYYY-MM-DD).""" - date_pattern = re.compile(r"^\d{4}-\d{2}-\d{2}$") - if not date_pattern.match(v): + if not _DATE_PATTERN.match(v): msg = f"Invalid date format: {v}. Expected ISO format (YYYY-MM-DD)" raise ValueError(msg) return vNote: The regex only validates format, not semantic validity (e.g.,
2025-02-30would pass). If calendar validity is needed, consider usingdatetime.strptimeinstead.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
drift/day_close/2025-12-20.yaml(1 hunks)src/onex_change_control/__init__.py(1 hunks)src/onex_change_control/enums/__init__.py(1 hunks)src/onex_change_control/enums/enum_drift_category.py(1 hunks)src/onex_change_control/enums/enum_evidence_kind.py(1 hunks)src/onex_change_control/enums/enum_interface_surface.py(1 hunks)src/onex_change_control/enums/enum_invariant_status.py(1 hunks)src/onex_change_control/enums/enum_pr_state.py(1 hunks)src/onex_change_control/models/__init__.py(1 hunks)src/onex_change_control/models/model_day_close.py(1 hunks)src/onex_change_control/models/model_ticket_contract.py(1 hunks)tests/test_models.py(1 hunks)tests/test_yaml_parsing.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
tests/test_yaml_parsing.py (2)
src/onex_change_control/models/model_day_close.py (1)
ModelDayClose(88-147)src/onex_change_control/models/model_ticket_contract.py (1)
ModelTicketContract(55-110)
src/onex_change_control/models/model_ticket_contract.py (3)
src/onex_change_control/enums/enum_evidence_kind.py (1)
EnumEvidenceKind(10-38)src/onex_change_control/enums/enum_interface_surface.py (1)
EnumInterfaceSurface(10-38)src/onex_change_control/models/model_day_close.py (1)
validate_schema_version(132-137)
src/onex_change_control/enums/__init__.py (5)
src/onex_change_control/enums/enum_drift_category.py (1)
EnumDriftCategory(10-42)src/onex_change_control/enums/enum_evidence_kind.py (1)
EnumEvidenceKind(10-38)src/onex_change_control/enums/enum_interface_surface.py (1)
EnumInterfaceSurface(10-38)src/onex_change_control/enums/enum_invariant_status.py (1)
EnumInvariantStatus(10-30)src/onex_change_control/enums/enum_pr_state.py (1)
EnumPRState(10-26)
src/onex_change_control/models/__init__.py (2)
src/onex_change_control/models/model_day_close.py (1)
ModelDayClose(88-147)src/onex_change_control/models/model_ticket_contract.py (1)
ModelTicketContract(55-110)
src/onex_change_control/models/model_day_close.py (4)
src/onex_change_control/enums/enum_drift_category.py (1)
EnumDriftCategory(10-42)src/onex_change_control/enums/enum_invariant_status.py (1)
EnumInvariantStatus(10-30)src/onex_change_control/enums/enum_pr_state.py (1)
EnumPRState(10-26)src/onex_change_control/models/model_ticket_contract.py (1)
validate_schema_version(94-99)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: claude-review
🔇 Additional comments (17)
src/onex_change_control/enums/enum_evidence_kind.py (1)
1-38: LGTM!Clean enum implementation with proper
@uniquedecorator,strinheritance for serialization, and comprehensive docstrings. The__str__method correctly returns the underlying value for YAML/JSON compatibility.src/onex_change_control/enums/enum_pr_state.py (1)
1-26: LGTM!Consistent implementation pattern with the other enums. The two-state model (merged/open) is appropriate for daily close report tracking.
src/onex_change_control/__init__.py (1)
11-19: LGTM!Clean public API exposure. The package exports the two main models while keeping implementation details (sub-models, enums) accessible via subpackages for users who need them.
tests/test_yaml_parsing.py (1)
32-70: LGTM with minor observation.Good approach creating inline test data rather than relying on template files with placeholders. The test validates the critical fields and the model's constraint checking.
Consider adding an assertion on
evidence_requirementscontent (e.g.,assert contract.evidence_requirements[0].kind.value == "tests") for completeness, but this is optional since the model validation already confirms the structure is correct.src/onex_change_control/enums/enum_invariant_status.py (1)
1-30: LGTM!Clean implementation consistent with the other enums. The
# noqa: S105suppression on line 19 is appropriate—Bandit flags "pass" as a potential hardcoded password, but here it's clearly a status value.src/onex_change_control/enums/__init__.py (1)
1-18: LGTM!Clean package initialization with proper re-exports and
__all__declaration. The absolute imports align with Python best practices for package modules.src/onex_change_control/models/__init__.py (1)
1-12: LGTM!Consistent package structure mirroring the enums module. Public API surface is well-defined.
src/onex_change_control/models/model_ticket_contract.py (3)
18-27: LGTM!Simple, well-documented model with appropriate field types and descriptions.
29-53: LGTM!Good use of
model_validator(mode="after")for conditional field requirements. The validation logic correctly enforces thatjustificationandfollow_up_ticket_idare non-empty when bypass is enabled.
101-110: Verify: asymmetric constraint is intentional.The validator prevents
interfaces_touchedwheninterface_change=False, but doesn't requireinterfaces_touchedto be non-empty wheninterface_change=True. This allows marking a ticket as changing interfaces without specifying which surfaces. Confirm this is the intended behavior.src/onex_change_control/enums/enum_interface_surface.py (1)
1-38: LGTM!Well-structured enum with proper
@uniquedecorator,strmixin for serialization compatibility, and comprehensive docstrings. Consistent with the other enums in this PR.src/onex_change_control/enums/enum_drift_category.py (1)
1-42: LGTM!Consistent enum implementation following the established pattern. Well-documented drift categories with clear semantic meanings.
tests/test_models.py (2)
27-135: LGTM!The
TestModelDayCloseclass provides comprehensive coverage for theModelDayClosemodel:
- Valid construction with minimal required fields
- Invalid schema version format validation
- Invalid date format validation
- Complete construction with all nested models populated
The test assertions are appropriate and match the expected model behavior.
137-225: LGTM!The
TestModelTicketContractclass provides thorough validation coverage:
- Valid construction with defaults
- Schema version validation
- Cross-field constraint validation (
interface_changevsinterfaces_touched)- Emergency bypass nested model validation for both required fields
- Complete construction with all optional fields populated
Good practice using a named constant for
expected_interfaces_countto avoid magic numbers.src/onex_change_control/models/model_day_close.py (2)
1-16: LGTM!Good practices observed:
- Module-level compiled regex
_SEMVER_PATTERNavoids recompilation on each validation call- Imports are minimal and schema-pure (only Pydantic and local enums)
19-86: LGTM!The sub-models are well-designed:
- Clear field descriptions for documentation
- Appropriate use of
ge=1constraint on PR numbers (line 37)- Correct use of
default_factory=listfor mutable defaults- Enum types properly referenced for typed fields
drift/day_close/2025-12-20.yaml (1)
1-132: LGTM!The day close report structure correctly aligns with the
ModelDayCloseschema:
- Valid SemVer
schema_versionand ISO date format- All nested structures match their corresponding Pydantic models
- Enum values (
merged,open,interfaces,pass,unknown) are valid- The report effectively documents the schema models implementation work and related drift resolutions
PR Review: M1 - Pydantic Schema Models v1SummaryThis PR successfully implements foundational Pydantic schema models for the ONEX drift control system. The implementation follows the repository's naming conventions and provides a solid foundation for machine-checkable drift detection artifacts. ✅ Strengths1. Excellent Naming Convention Adherence
2. Schema Purity Maintained
3. Strong Validation Logic
4. Good Test Coverage (97%)
5. Well-Documented Models
🔍 Code Quality Issues & Recommendations1. Duplicate Field Descriptions (Minor)Location: Issue: Field description is duplicated in both the Recommendation: Remove one of the duplicate descriptions. The standard Pydantic pattern is to use one or the other, not both. Same issue in: 2. Potential SemVer Pattern Limitation (Medium)Location: Issue: Current pattern only supports basic SemVer (e.g., "1.0.0") but doesn't support:
Recommendation:
Same issue in: 3. Missing Inverse Validation Logic (Medium)Location: Issue: The inverse is not validated. If Recommendation: Consider adding validation for the inverse case and add test coverage. 4. Noqa Comment Needs Better Explanation (Minor)Location: Issue: The Recommendation: Add an inline explanation like: 5. Date Validation Could Be Stricter (Low)Location: Issue: Current regex accepts invalid dates like "2025-99-99" or "2025-02-30". Recommendation: Use Python's 🔒 Security Considerations✅ No Critical Security Issues Found
Minor Security Recommendations:
⚡ Performance Considerations✅ No Performance Issues
🧪 Test Coverage AnalysisCurrent Coverage: 97% ✅Well-tested areas:
Missing test coverage suggestions:
📝 Documentation Suggestions
🎯 Summary & RecommendationsMust Fix Before Merge:None - the code is functional and well-structured. Should Fix (High Priority):
Nice to Have (Medium Priority):
Low Priority:
✨ Overall AssessmentRecommendation: ✅ APPROVE with suggested improvements This is excellent foundational work that successfully delivers on all stated acceptance criteria:
The code quality is high, follows best practices, and provides a solid foundation for the drift control system. The identified issues are mostly minor refinements that can be addressed in follow-up work or before merge, depending on team preference. Great work! 🚀 |
…validation High Priority fixes: 1. Remove duplicate Field() definitions for schema_version - Removed redundant Annotated + Field() pattern - Simplified to single Field() definition 2. Move date pattern to module level for performance - Compiled regex at module level (like _SEMVER_PATTERN) - Added documentation about format-only validation 3. Improve SemVer pattern documentation - Added comments explaining basic SemVer support only - Documented limitations (no pre-release/build metadata) 4. Improve noqa comment explanation - Added inline explanation for S105 suppression 5. Add file existence guard for YAML test - Added pytest.mark.skipif for missing YAML fixture - Prevents test failures if file is removed/modified 6. Add missing test coverage - Added test for PR number validation (ge=1 constraint) - Added test for interface_change=True with empty interfaces - Used specific ValidationError instead of generic Exception All tests passing (14/14) All linting checks passing Refs: PR #4 review feedback
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/onex_change_control/models/model_ticket_contract.py (1)
13-17: Consider extracting the SemVer pattern to a shared module.The
_SEMVER_PATTERNregex is duplicated identically inmodel_day_close.py(line 18). Extract to a shared constants or validators module to maintain DRY and ensure consistent validation across models.🔎 Suggested approach
Create a shared module, e.g.,
src/onex_change_control/validators.py:import re # SemVer pattern for schema_version validation # Note: This pattern supports basic SemVer (major.minor.patch) only. SEMVER_PATTERN = re.compile(r"^\d+\.\d+\.\d+$") def validate_semver(v: str, field_name: str = "schema_version") -> str: """Validate a string is SemVer format.""" if not SEMVER_PATTERN.match(v): msg = f"Invalid {field_name} format: {v}. Expected SemVer (e.g., '1.0.0')" raise ValueError(msg) return vThen import and use in both model files.
tests/test_models.py (1)
214-228: Consider adding a test for valid emergency bypass when enabled.The current tests validate that invalid configurations raise errors, but there's no test confirming a valid enabled bypass succeeds. This would complete the positive/negative test pair.
🔎 Suggested test
def test_emergency_bypass_valid_when_enabled(self) -> None: """Test valid emergency bypass configuration when enabled.""" bypass = ModelEmergencyBypass( enabled=True, justification="Critical production fix required", follow_up_ticket_id="OMN-999", ) assert bypass.enabled is True assert bypass.justification == "Critical production fix required" assert bypass.follow_up_ticket_id == "OMN-999"
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/onex_change_control/enums/enum_invariant_status.py(1 hunks)src/onex_change_control/models/model_day_close.py(1 hunks)src/onex_change_control/models/model_ticket_contract.py(1 hunks)tests/test_models.py(1 hunks)tests/test_yaml_parsing.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/test_yaml_parsing.py
🧰 Additional context used
🧬 Code graph analysis (1)
src/onex_change_control/models/model_ticket_contract.py (3)
src/onex_change_control/enums/enum_evidence_kind.py (1)
EnumEvidenceKind(10-38)src/onex_change_control/enums/enum_interface_surface.py (1)
EnumInterfaceSurface(10-38)src/onex_change_control/models/model_day_close.py (1)
validate_schema_version(133-138)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: claude-review
🔇 Additional comments (9)
src/onex_change_control/enums/enum_invariant_status.py (1)
1-30: LGTM!Clean enum implementation following consistent patterns:
strmixin enables JSON serialization@uniqueprevents duplicate values- The
# noqa: S105correctly suppresses the Bandit false positive for "pass"__str__method aligns with other enums in the codebasesrc/onex_change_control/models/model_ticket_contract.py (2)
31-54: LGTM!The
ModelEmergencyBypassvalidator correctly enforces business rules:justificationandfollow_up_ticket_idare required whenenabled=True. Using empty string defaults with validation is a clean approach.
57-117: LGTM!Well-structured model with clear validation:
- SemVer validation for
schema_version- Proper constraint enforcement between
interface_changeandinterfaces_touched- Good documentation in the validator explaining the asymmetric constraint
tests/test_models.py (2)
28-165: LGTM!Comprehensive test coverage for
ModelDayClose:
- Validates SemVer and date format constraints
- Tests nested model construction
- Covers PR number boundary validation (ge=1)
257-273: LGTM!Good practice documenting intentional edge-case behavior with a dedicated test. The docstring clearly explains the business rationale for allowing
interface_change=Truewith an emptyinterfaces_touchedlist.src/onex_change_control/models/model_day_close.py (4)
14-21: LGTM on pattern compilation.Compiling regex patterns at module level is the right approach for performance. The comments clearly document the SemVer limitation.
140-151: Date format validation allows invalid calendar dates.As documented, "2025-02-30" passes validation. If calendar validity is eventually needed, consider using
datetime.strptimeor Pydantic's built-indatetype.The current format-only validation is acceptable if downstream consumers handle invalid dates gracefully. Verify this is intentional for your use case.
24-91: LGTM!Clean nested model hierarchy with:
- Consistent use of
Field(...)for required fields- Proper
default_factory=listfor collection defaults- Clear descriptions on all fields
- Appropriate use of enum types for constrained values
93-129: LGTM!
ModelDayCloseis well-designed:
- Required fields (
schema_version,date,invariants_checked) enforce minimum viable reports- Optional list fields default to empty, allowing incremental population
- Field descriptions provide good documentation
PR Review: M1 - Pydantic Schema Models v1OverviewThis PR implements comprehensive Pydantic schema models for Day Close and Ticket Contract artifacts, achieving the stated goal of machine-checkable drift control schemas. The implementation is well-structured, follows established conventions, and includes solid test coverage. ✅ StrengthsCode Quality & Best Practices
Test Coverage
Security & Validation
🔍 Issues & Suggestions1. Date Validation
|
…rity constraints, immutability Must Fix: - Date validation: Use datetime.date.fromisoformat() for calendar-valid date checking - Replaced regex-only validation with format check + calendar validation - Rejects invalid dates like 2025-02-30 or 2025-13-01 Should Consider (Implemented): - Security: Add max length/items constraints to prevent DoS - Added _MAX_STRING_LENGTH (10000) and _MAX_LIST_ITEMS (1000) constants - Applied max_length constraints to all string fields - Applied max_length constraints to all list fields - max_length constraints are enforced by Pydantic - Immutability: Added frozen=True to all model configs - All models are now immutable after creation - Enables thread-safe access and safe use as dictionary keys - Documented immutability guarantees in model docstrings - Interface change validation: Documented steady state vs temporary - Added comprehensive documentation about interface_change=True with empty interfaces_touched - Clarified that empty list is allowed temporarily but should be populated before completion Nice to Have (Implemented): - Test coverage: Added comprehensive tests - Calendar-invalid dates (February 30th, month 13, etc.) - SemVer edge cases (leading zeros, missing components, pre-release versions) - Boundary conditions (max length, max items) - PR number validation (ge=1 constraint) - Serialization tests: Added round-trip serialization tests - JSON round-trip tests for both models - YAML round-trip tests for both models - Enum serialization verification - Frozen model immutability tests - Security constraint tests: Added DoS prevention tests - String length constraint tests - List length constraint tests - Ticket ID length constraint tests All 29 tests passing All linting checks passing All type checks passing Refs: PR #4 additional review feedback
Code Review: PR #4 - M1: Pydantic Schema Models v1SummaryThis is a high-quality implementation that successfully delivers machine-checkable schemas for Day Close and Ticket Contract artifacts. The code demonstrates strong engineering practices with comprehensive test coverage (97%), excellent documentation, and thoughtful security constraints. ✅ Strengths1. Code Quality & Best Practices
2. Security
3. Test Coverage (97%)
4. Validation Logic
5. Documentation
🔍 Issues & Recommendations1. Potential Bug: Redundant String Length Validation (Priority: Low)Location: class ModelDayClosePlanItem(BaseModel):
requirement_id: str = Field(..., max_length=_MAX_STRING_LENGTH)
summary: str = Field(..., max_length=_MAX_STRING_LENGTH)
@model_validator(mode="after")
def validate_string_lengths(self) -> "ModelDayClosePlanItem":
"""Validate string lengths to prevent DoS attacks."""
if len(self.requirement_id) > _MAX_STRING_LENGTH:
raise ValueError(...)
if len(self.summary) > _MAX_STRING_LENGTH:
raise ValueError(...)
return selfIssue: The Impact:
Recommendation:
Note: This pattern only appears in 2. Performance: Redundant Regex Compilation (Priority: Low)Location: # model_day_close.py
_SEMVER_PATTERN = re.compile(r"^\d+\.\d+\.\d+$")
_DATE_PATTERN = re.compile(r"^\d{4}-\d{2}-\d{2}$")
# model_ticket_contract.py
_SEMVER_PATTERN = re.compile(r"^\d+\.\d+\.\d+$")Issue: The SemVer pattern is duplicated across files. While regex compilation is cached, this creates maintenance burden (if pattern needs updating, must change in 2 places). Impact: Minor - performance is already optimized via module-level compilation, but DRY principle violated. Recommendation:
3. SemVer Validation Limitations (Priority: Info)Location: The inline comments already acknowledge this limitation, which is good: # Note: This pattern supports basic SemVer (major.minor.patch) only.
# Pre-release versions (e.g., "1.0.0-alpha") and build metadata (e.g., "1.0.0+build")
# are not supported. If full SemVer support is needed, consider using a SemVer library.Additional Issues:
Recommendation:
4. Test Coverage: Potential False Positive (Priority: Info)Location: def test_excessive_string_length(self) -> None:
"""Test that strings exceeding limit are rejected."""
too_long_string = "x" * (_MAX_STRING_LENGTH + 1)
try:
item = ModelDayClosePlanItem(...)
# If validation passes, skip the test rather than fail
pytest.skip("max_length constraint not enforced...")
except (ValidationError, ValueError) as e:
# Expected behavior
assert "max length" in str(e).lower() or ...Issue: This test uses Impact: 97% coverage may be misleading if tests are being skipped. Recommendation:
5. Minor: Missing Type Hint for Enum
|
| Test Suite | Lines | Coverage Focus |
|---|---|---|
test_models.py |
360 | Structural validation, edge cases, business rules |
test_security_constraints.py |
147 | DoS prevention, length limits |
test_serialization.py |
300 | JSON/YAML round-trip, immutability |
test_yaml_parsing.py |
75 | Real-world YAML parsing |
| Total | 882 lines | 97% coverage |
Missing Coverage Areas (if any):
- Error messages for all validation failures (could add snapshot tests)
- Enum edge cases (e.g., case-insensitivity) - though Pydantic handles this
- Performance tests for max list size (1000 items) - already covered in
test_security_constraints.py:77-96
Verdict: Excellent test coverage with real-world validation.
🔐 Security Review
Threat Model
| Threat | Mitigation | Status |
|---|---|---|
| DoS via large strings | max_length=10000 on all string fields |
✅ Mitigated |
| DoS via large lists | max_length=1000 on all list fields |
✅ Mitigated |
| Injection attacks | Pydantic validation, no dynamic execution | ✅ Not vulnerable |
| Schema poisoning | SemVer validation, frozen models | ✅ Mitigated |
| Type confusion | Strict Pydantic validation | ✅ Mitigated |
Verdict: Security is well-addressed for a schema validation library.
🎯 Acceptance Criteria Validation
From PR description:
| Criterion | Status | Evidence |
|---|---|---|
| ✅ Unit tests cover core structural validation | PASS | 12 tests, 97% coverage |
| ✅ Models are schema-pure | PASS | Only Pydantic + local enum imports |
| ✅ Naming conventions aligned | PASS | Enum* and Model* prefixes used |
✅ schema_version validated as SemVer |
PASS | Regex validation (basic SemVer) |
Verdict: All acceptance criteria met.
🚀 Overall Verdict
APPROVED with minor recommendations for future improvement.
This PR demonstrates excellent engineering practices:
- Clean, maintainable code with strong type safety
- Comprehensive test coverage with real-world validation
- Thoughtful security constraints
- Clear documentation throughout
The identified issues are minor and don't block merge. They can be addressed in follow-up PRs if desired.
Recommended Next Steps (from PR description):
- ✅ Merge this PR
- Proceed with OMN-963 (JSON Schema export script)
- Proceed with OMN-965 (YAML validation CLI)
- Consider addressing the redundant validator in
ModelDayClosePlanItemin a cleanup PR
Great work on M1! 🎉 The schema foundation is solid and ready for downstream consumption.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
tests/test_security_constraints.py (1)
17-19: Consider importing constants from the source module to avoid drift.These constants duplicate the values defined in
model_day_close.py. If the source values change, these tests could silently become out-of-sync.🔎 Proposed fix
-# Security constraint limits -_MAX_STRING_LENGTH = 10000 -_MAX_LIST_ITEMS = 1000 +from onex_change_control.models.model_day_close import ( + _MAX_STRING_LENGTH, + _MAX_LIST_ITEMS, +)Note: This requires exporting these constants from
model_day_close.py(they're already module-level, so they should be importable). Alternatively, define them in a shared constants module.src/onex_change_control/models/model_day_close.py (1)
62-72: Redundant validation:max_lengthis already enforced by Pydantic v2.The
model_validatorduplicates themax_lengthconstraint already defined on theFielddeclarations (lines 56, 59). Pydantic v2 enforcesmax_lengthbefore the model validator runs, so this check will never trigger.🔎 Proposed fix: Remove redundant validator
class ModelDayClosePlanItem(BaseModel): """Plan item in daily close report.""" model_config = ConfigDict(frozen=True) requirement_id: str = Field( ..., description="Requirement identifier", max_length=_MAX_STRING_LENGTH ) summary: str = Field( ..., description="Summary of the requirement", max_length=_MAX_STRING_LENGTH ) - - @model_validator(mode="after") - def validate_string_lengths(self) -> "ModelDayClosePlanItem": - """Validate string lengths to prevent DoS attacks.""" - if len(self.requirement_id) > _MAX_STRING_LENGTH: - msg = f"requirement_id exceeds max length of {_MAX_STRING_LENGTH}" - raise ValueError(msg) - if len(self.summary) > _MAX_STRING_LENGTH: - msg = f"summary exceeds max length of {_MAX_STRING_LENGTH}" - raise ValueError(msg) - return selfIf you prefer defense-in-depth, keep it but add a comment explaining the intentional redundancy.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
src/onex_change_control/models/model_day_close.py(1 hunks)src/onex_change_control/models/model_ticket_contract.py(1 hunks)tests/test_models.py(1 hunks)tests/test_security_constraints.py(1 hunks)tests/test_serialization.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- tests/test_models.py
- src/onex_change_control/models/model_ticket_contract.py
🧰 Additional context used
🧬 Code graph analysis (3)
tests/test_security_constraints.py (3)
src/onex_change_control/enums/enum_invariant_status.py (1)
EnumInvariantStatus(10-30)src/onex_change_control/models/model_day_close.py (3)
ModelDayClose(160-241)ModelDayCloseInvariantsChecked(128-144)ModelDayClosePlanItem(50-71)src/onex_change_control/models/model_ticket_contract.py (2)
ModelEmergencyBypass(40-67)ModelTicketContract(70-156)
tests/test_serialization.py (7)
src/onex_change_control/enums/enum_drift_category.py (1)
EnumDriftCategory(10-42)src/onex_change_control/enums/enum_evidence_kind.py (1)
EnumEvidenceKind(10-38)src/onex_change_control/enums/enum_interface_surface.py (1)
EnumInterfaceSurface(10-38)src/onex_change_control/enums/enum_invariant_status.py (1)
EnumInvariantStatus(10-30)src/onex_change_control/enums/enum_pr_state.py (1)
EnumPRState(10-26)src/onex_change_control/models/model_day_close.py (3)
ModelDayClose(160-241)ModelDayClosePlanItem(50-71)ModelDayClosePR(74-86)src/onex_change_control/models/model_ticket_contract.py (3)
ModelEmergencyBypass(40-67)ModelEvidenceRequirement(24-37)ModelTicketContract(70-156)
src/onex_change_control/models/model_day_close.py (4)
src/onex_change_control/enums/enum_drift_category.py (1)
EnumDriftCategory(10-42)src/onex_change_control/enums/enum_invariant_status.py (1)
EnumInvariantStatus(10-30)src/onex_change_control/enums/enum_pr_state.py (1)
EnumPRState(10-26)src/onex_change_control/models/model_ticket_contract.py (1)
validate_schema_version(125-130)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: claude-review
🔇 Additional comments (20)
tests/test_security_constraints.py (4)
77-96: LGTM!Good boundary test validating that exactly 1000 items (the limit) are accepted.
98-117: LGTM!Correctly tests that lists exceeding the 1000-item limit are rejected.
123-146: LGTM!Good test for ticket_id length constraint: valid 7-char ID passes, 54-char ID (exceeding 50-char limit) is rejected.
36-71: Test approach is sound and appropriately defensive given Pydantic v2 enforcement.Pydantic v2 enforces
max_lengthon string fields, so the validation will reliably fail.ModelDayClosePlanItemincludes bothField(max_length=_MAX_STRING_LENGTH)constraints and a redundant@model_validator(mode="after")that checks the same lengths. The test correctly validates this defense-in-depth approach using try/except withpytest.skip()as a fallback. The fallback documents expected behavior without introducing false failures, making the test a solid defensive pattern.tests/test_serialization.py (8)
1-26: LGTM!Imports are well-organized and all appear to be used in the tests.
32-63: LGTM!Solid JSON round-trip test using proper Pydantic v2 serialization methods.
65-106: LGTM!Correct YAML round-trip pattern using
model_dump(mode="json")to ensure JSON-serializable output.
108-152: LGTM!Comprehensive enum serialization test verifying both JSON string representation and proper deserialization back to enum values.
158-194: LGTM!Proper JSON round-trip test for
ModelTicketContractwith nested structures.
196-228: LGTM!Good YAML round-trip test including the enabled emergency bypass scenario with justification and follow-up ticket.
230-263: LGTM!Thorough enum serialization test for ticket contract enums.
266-299: LGTM!Good immutability tests verifying that frozen models raise
ValidationErrorwhen modification is attempted. Thetype: ignorecomments are appropriate since the type checker would otherwise flag the invalid assignments.src/onex_change_control/models/model_day_close.py (8)
15-27: LGTM!Well-documented constants with clear comments explaining the SemVer pattern limitations.
30-48: LGTM!Clean frozen model with appropriate max_length constraints.
74-87: LGTM!Clean model with appropriate constraints including
ge=1for positive PR numbers.
89-102: LGTM!Proper nested structure with list length constraint.
104-126: LGTM!Well-structured drift detection model with consistent constraints.
128-145: LGTM!Clean invariants model with appropriate enum types.
147-158: LGTM!Simple and clean risk model.
212-241: LGTM!Well-implemented validators with clear error messages. The two-step date validation (format check first, then calendar validity) provides better error messages for users.
| corrections_for_tomorrow: list[str] = Field( | ||
| default_factory=list, | ||
| description="Actionable corrections for tomorrow", | ||
| max_length=_MAX_LIST_ITEMS, | ||
| ) |
There was a problem hiding this comment.
Missing max_length constraint on corrections_for_tomorrow string items.
Unlike other string fields in this model, individual items in corrections_for_tomorrow don't have a length constraint. A malicious payload could include very long strings within this list, bypassing your DoS protections.
🔎 Proposed fix: Add item-level constraint
You can use Annotated with StringConstraints to limit individual string lengths:
+from typing import Annotated
+from pydantic import StringConstraints
+
+# Type alias for constrained strings
+_ConstrainedStr = Annotated[str, StringConstraints(max_length=_MAX_STRING_LENGTH)]
+
class ModelDayClose(BaseModel):
# ...
corrections_for_tomorrow: list[str] = Field(
- default_factory=list,
- description="Actionable corrections for tomorrow",
- max_length=_MAX_LIST_ITEMS,
+ corrections_for_tomorrow: list[_ConstrainedStr] = Field(
+ default_factory=list,
+ description="Actionable corrections for tomorrow",
+ max_length=_MAX_LIST_ITEMS,
)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/onex_change_control/models/model_day_close.py around lines 201 to 205,
the list field corrections_for_tomorrow has a max_length on the list but no
per-item string length constraint; update the type to an Annotated/Constrained
string (e.g., Annotated[str, Field(max_length=<same_max_as_other_strings>)]) or
use pydantic.constr for items, preserve default_factory=list and the list
max_length, and import Annotated/constr as needed so each list element is
limited to the same per-string max length used elsewhere to prevent oversized
entries.
Actionable fixes: 1. Remove redundant @model_validator from ModelDayClosePlanItem - Pydantic v2 already enforces max_length constraints before validators run - The validator was redundant and would never trigger - Removed model_validator import as it's no longer needed 2. Import constants instead of duplicating them - Import _MAX_STRING_LENGTH and _MAX_LIST_ITEMS from model_day_close - Prevents test constants from drifting out of sync with source - Follows DRY principle All 29 tests passing All linting checks passing All type checks passing Refs: PR #4 review feedback
Code Review: M1 Pydantic Schema Models ImplementationSummaryThis PR successfully implements Pydantic schema models for Day Close and Ticket Contract artifacts with excellent attention to detail, comprehensive testing, and strong security considerations. Strengths1. Excellent Code Quality
2. Comprehensive Testing (97% coverage, 29 tests)
3. Security Considerations
4. Performance Optimizations
Areas for Improvement (All Minor)1. SemVer Validation Limitations (Minor)
2. String Length Enforcement Uncertainty (Low)
3. Test Coverage Gap (Very Low)
Security Review
No Security Concerns Identified Performance Review
No Performance Concerns Test Coverage: 97% (29 tests)
Recommendations SummaryMust Address Before Merge
Should Consider (Non-Blocking)
Nice-to-Have (Future Work)
Final VerdictAPPROVED - This is excellent work that meets all acceptance criteria:
Merge Recommendation: APPROVE & MERGEThis PR demonstrates exceptional attention to detail with iterative improvements based on feedback. The minor observations above are suggestions for future enhancement, not blockers. Great work! Next Steps (Post-Merge)
|
…omplete property
Should Consider (Non-Blocking) - Implemented:
1. Strengthen SemVer validation to reject leading zeros
- Updated regex pattern from \d+ to (0|[1-9]\d*) to reject leading zeros
- Pattern now rejects "01.0.0", "1.00.0", "1.0.01" per SemVer spec
- Zero itself ("0.0.0") is still valid (not a leading zero)
- Updated tests to verify leading zeros are rejected
2. Verify string max_length enforcement by removing pytest.skip()
- Changed test to use pytest.raises(ValidationError) instead of try/except with skip
- Confirms Pydantic v2 enforces max_length constraints correctly
- Test now properly validates DoS prevention
Nice-to-Have (Future Work) - Implemented:
3. Add is_complete property to ModelTicketContract
- Property checks if contract is in a complete, steady state
- Returns False if interface_change=True with empty interfaces_touched
- Returns True otherwise (interface_change=False or interfaces populated)
- Emergency bypass completeness already validated by ModelEmergencyBypass
4. Add test for emergency bypass edge cases
- Test for enabled=True with empty justification (should fail)
- Test for enabled=True with empty follow_up_ticket_id (should fail)
- Test for enabled=False with empty strings (should pass)
- Test for enabled=True with whitespace-only strings (should pass)
All 31 tests passing (added 2 new tests)
All linting checks passing
All type checks passing
Refs: PR #4 additional review feedback
Code Review: M1 Pydantic Schema Models ImplementationOverall AssessmentSummary: This is a high-quality implementation with excellent software engineering practices. The PR successfully implements machine-checkable schemas for Day Close and Ticket Contract artifacts with 97% test coverage and strong attention to security, immutability, and validation. Recommendation: ✅ Approve with minor suggestions Strengths1. Excellent Code Quality
2. Security Considerations ⭐
3. Validation Logic
4. Test Coverage ⭐
5. Schema Purity
Issues & Suggestions1. Potential Bug: Whitespace-only strings in emergency bypass📍 Location: src/onex_change_control/models/model_ticket_contract.py:58-68 Issue: The validator checks if not self.justification: which allows whitespace-only strings to pass. Recommendation: Use .strip() to reject whitespace-only strings. Test at tests/test_models.py:456-463 currently documents this as should pass, but this seems unintentional based on the business logic. 2. Code Duplication: SemVer Pattern📍 Locations: model_day_close.py:15-20, model_ticket_contract.py:17-22 Issue: The _SEMVER_PATTERN regex and comments are duplicated across both files. Recommendation: Extract to a shared validation module for better maintainability. 3. Test Organization Opportunity📍 Location: tests/test_models.py (464 lines) Observation: Consider splitting into separate test files as the codebase grows: test_model_day_close.py and test_model_ticket_contract.py. This is optional for now but will help maintainability. 4. Documentation: SemVer LimitationsSuggestion: Consider adding a note in the model docstrings that schema_version only supports basic SemVer (no pre-release/build metadata). Performance Considerations✅ Excellent:
Best Practices ObservationsExcellent Patterns:
Test Coverage AnalysisCovered:
API DesignStrengths:
The is_complete property is a computed property that does not trigger validation errors. This is intentional and well-documented, allowing work in progress contracts. Good design choice. Security Review✅ No security concerns identified
Alignment with Repository Conventions✅ Fully aligned with omnibase_core conventions:
Final Recommendation✅ Approve with minor suggestions This is excellent work that demonstrates strong software engineering fundamentals: comprehensive testing, security awareness, clean architecture, and excellent documentation. The suggestions above are minor improvements that can be addressed in follow-up PRs if preferred. None are blocking issues. Estimated merge readiness: Ready to merge after addressing the whitespace validation bug (Issue 1) or documenting it as intentional behavior. Questions for Author
Great work! 🎉 |
…cumentation Bug Fixes: 1. Fix whitespace-only strings in emergency bypass validation - Changed validation to use .strip() to reject whitespace-only strings - Whitespace-only justification or follow_up_ticket_id now properly rejected - Updated test to verify whitespace-only strings are rejected (not accepted) - Fixes potential bug where " " would pass validation Code Quality: 2. Extract SemVer pattern to shared validation module - Created src/onex_change_control/validation/patterns.py - Moved SEMVER_PATTERN to shared module to eliminate duplication - Both model_day_close.py and model_ticket_contract.py now import from shared module - Follows DRY principle and improves maintainability Documentation: 3. Add SemVer limitations documentation to model docstrings - Added "Schema Version" section to ModelDayClose docstring - Added "Schema Version" section to ModelTicketContract docstring - Documents that only basic SemVer (major.minor.patch) is supported - Notes that pre-release versions and build metadata are not supported - Clarifies that leading zeros are rejected per SemVer spec All 31 tests passing All linting checks passing All type checks passing Refs: PR #4 final review feedback
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
src/onex_change_control/models/model_day_close.py (1)
191-195: Missing per-item string length constraint (previously flagged).As noted in a previous review, individual strings in the
corrections_for_tomorrowlist lackmax_lengthconstraints. While the list itself is limited to_MAX_LIST_ITEMS, each string could be arbitrarily long, potentially bypassing DoS protections.🔎 Proposed fix using Annotated with StringConstraints
+from typing import Annotated +from pydantic import StringConstraints + +# Type alias for constrained strings +_ConstrainedStr = Annotated[str, StringConstraints(max_length=_MAX_STRING_LENGTH)] + class ModelDayClose(BaseModel): # ... - corrections_for_tomorrow: list[str] = Field( + corrections_for_tomorrow: list[_ConstrainedStr] = Field( default_factory=list, description="Actionable corrections for tomorrow", max_length=_MAX_LIST_ITEMS, )
🧹 Nitpick comments (2)
src/onex_change_control/models/model_day_close.py (1)
202-214: Consider extracting shared validator to reduce duplication.This
validate_schema_versionmethod is identical to the one inmodel_ticket_contract.py(lines 126-138). Consider extracting it to a shared validator function in the validation package.🔎 Suggested refactoring approach
Add to
src/onex_change_control/validation/patterns.py:def validate_semver(v: str) -> str: """Validate that a string matches basic SemVer format. Note: Only basic SemVer (major.minor.patch) is supported. Pre-release versions and build metadata are not supported. Leading zeros are rejected per SemVer specification. """ if not SEMVER_PATTERN.match(v): msg = f"Invalid schema_version format: {v}. Expected SemVer (e.g., '1.0.0')" raise ValueError(msg) return vThen use it in both model files:
from onex_change_control.validation.patterns import validate_semver class ModelDayClose(BaseModel): # ... @field_validator("schema_version") @classmethod def validate_schema_version(cls, v: str) -> str: """Validate schema_version is SemVer format.""" return validate_semver(v)src/onex_change_control/models/model_ticket_contract.py (1)
1-14: LGTM! Clean imports and security constraints.The module maintains schema purity with appropriate imports.
Optional: Consider centralizing security constants.
The constants
_MAX_STRING_LENGTHand_MAX_LIST_ITEMSare duplicated betweenmodel_day_close.pyand this file. If these constraints should be consistent across all models, consider extracting them to a shared location likevalidation/patterns.pyor a newconstants.pymodule.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
src/onex_change_control/models/model_day_close.py(1 hunks)src/onex_change_control/models/model_ticket_contract.py(1 hunks)src/onex_change_control/validation/__init__.py(1 hunks)src/onex_change_control/validation/patterns.py(1 hunks)tests/test_models.py(1 hunks)tests/test_security_constraints.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- tests/test_security_constraints.py
- tests/test_models.py
🧰 Additional context used
🧬 Code graph analysis (2)
src/onex_change_control/models/model_day_close.py (4)
src/onex_change_control/enums/enum_drift_category.py (1)
EnumDriftCategory(10-42)src/onex_change_control/enums/enum_invariant_status.py (1)
EnumInvariantStatus(10-30)src/onex_change_control/enums/enum_pr_state.py (1)
EnumPRState(10-26)src/onex_change_control/models/model_ticket_contract.py (1)
validate_schema_version(128-138)
src/onex_change_control/models/model_ticket_contract.py (3)
src/onex_change_control/enums/enum_evidence_kind.py (1)
EnumEvidenceKind(10-38)src/onex_change_control/enums/enum_interface_surface.py (1)
EnumInterfaceSurface(10-38)src/onex_change_control/models/model_day_close.py (1)
validate_schema_version(204-214)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: claude-review
🔇 Additional comments (10)
src/onex_change_control/validation/patterns.py (1)
1-13: LGTM! Well-documented SemVer pattern.The regex correctly enforces basic SemVer format (major.minor.patch) with no leading zeros, and the documentation clearly explains the limitations regarding pre-release versions and build metadata.
src/onex_change_control/validation/__init__.py (1)
1-5: LGTM! Clean package initialization.The re-export pattern correctly exposes SEMVER_PATTERN through the validation package namespace.
src/onex_change_control/models/model_day_close.py (4)
1-22: LGTM! Clean imports and well-defined security constraints.The module maintains schema purity by importing only Pydantic and local enums, and the security constants provide clear DoS protection.
25-142: LGTM! Well-structured nested models with proper constraints.All nested models consistently enforce immutability via
frozen=True, apply appropriatemax_lengthconstraints on strings and lists, and include clear field descriptions. Theprfield correctly usesge=1to prevent invalid PR numbers.
144-190: LGTM! Excellent model structure and documentation.The comprehensive docstring clearly explains the rationale for immutability and SemVer validation. Field definitions are consistent and properly constrained.
216-236: LGTM! Excellent two-step date validation.The validator provides clear error messages by first checking the format with regex, then validating calendar correctness with
date.fromisoformat(). The exception chaining preserves error context appropriately.src/onex_change_control/models/model_ticket_contract.py (4)
17-31: LGTM! Well-structured evidence requirement model.The model properly uses immutability, applies appropriate constraints, and correctly handles the optional
commandfield.
33-64: LGTM! Excellent validation logic for emergency bypass.The
model_validatorcorrectly enforces that whenenabled=True, bothjustificationandfollow_up_ticket_idmust be non-empty and not just whitespace. This prevents incomplete bypass configurations.
67-139: LGTM! Comprehensive model with excellent documentation.The model structure is well-designed with clear field definitions, appropriate constraints, and a comprehensive docstring that explains the validation rules and immutability rationale.
140-186: LGTM! Well-designed validation and completeness checking.The
validate_interface_constraintscorrectly enforces business rules while allowing temporary incomplete states during contract creation. Theis_completeproperty provides a clear API for checking completion status, and the logic correctly accounts for both interface change states.
PR Review: M1 - Pydantic Schema Models ImplementationSummaryThis PR implements comprehensive Pydantic schema models for drift control artifacts (Day Close reports and Ticket Contracts). The implementation is well-structured, thoroughly tested (97% coverage), and follows the repository's naming conventions. Overall, this is high-quality work ready for merge with only minor suggestions for future consideration. Strengths ✅1. Excellent Code Quality
2. Strong Security Posture
3. Comprehensive Test Coverage
4. Well-Designed Validation Logic
5. Thoughtful Design Decisions
Minor Suggestions 💡1. Type Annotation PrecisionIn 2. Pydantic Field ConfigurationConsider adding 3. Test OrganizationConsider adding a conftest.py with shared fixtures for common objects (e.g., Potential Issues
|
…nd improve template guidance [OMN-964] - Update day close YAML files (2025-12-21 through 2025-12-23) to include actual PRs worked on - 2025-12-21: Added PR #4 (merged) and PR #5 (open) - 2025-12-22: Added PR #5 (open) - 2025-12-23: Added PR #5 (merged) and PR #6 (open) - Improve day_close template: Change PR placeholder from pr: 1 to pr: 123 with clearer comment - Improve ticket_contract template: - Enhance unknown handling guidance to mention is_complete property behavior - Add examples for evidence requirements command optionality (null vs omit) All changes validate successfully against schema models.
…ful tests + state assertion
- Validate {repo} against ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ before substitution;
adversarial values return BLOCK instead of reaching sh -c
- test_check_command_placeholder_substitution now captures the executed command
and asserts 586/OmniNode-ai/omnidash appear and no literal {pr}/{repo} remain
- Add test_check_command_invalid_repo_blocks for the new validation path
- dod-001 in OMN-7996 and OMN-7998: pipe state through grep -E '^(OPEN|MERGED)$'
so CLOSED PRs fail the check
- pre-commit demotion now also triggers on CI=true env var (finding #4)
- Add test_check_command_precommit_skipped_in_ci for CI gate path
- Fix truncated summary in contracts/OMN-7996.yaml (finding #5)
…context [OMN-7996] [OMN-7998] (#257) * fix(ci): resolve contract_compliance_check detached-HEAD in PR merge context [OMN-7996, OMN-7998] - _check_command now substitutes {pr} and {repo} placeholders before execution, so contract YAML DoD checks don't rely on git branch context - pre-commit commands demoted to WARN (not BLOCK) when pre-commit is not installed in runner - contracts/OMN-7996.yaml and OMN-7998.yaml updated to use parameterized gh calls (gh pr view {pr} --repo {repo} ...) and gh pr checks without --watch - Two new tests: placeholder substitution and pre-commit-absent warn path * fix(ci): address hostile review findings — input validation + meaningful tests + state assertion - Validate {repo} against ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ before substitution; adversarial values return BLOCK instead of reaching sh -c - test_check_command_placeholder_substitution now captures the executed command and asserts 586/OmniNode-ai/omnidash appear and no literal {pr}/{repo} remain - Add test_check_command_invalid_repo_blocks for the new validation path - dod-001 in OMN-7996 and OMN-7998: pipe state through grep -E '^(OPEN|MERGED)$' so CLOSED PRs fail the check - pre-commit demotion now also triggers on CI=true env var (finding #4) - Add test_check_command_precommit_skipped_in_ci for CI gate path - Fix truncated summary in contracts/OMN-7996.yaml (finding #5) * chore: trigger CI re-run for pr-title check [OMN-7996] * fix(ci): address CodeRabbit findings — base branch check, workspace, tighter precommit gate [OMN-7996, OMN-7998] - dod-001: extend gh pr view to query baseRefName; awk gate fails if base != main - dod-003: switch from raw command to test_passes check type in both contracts - _check_command: pass workspace as cwd to subprocess so --workspace is honored - CI demotion: narrow from CI=true alone to (binary absent AND in CI); installed pre-commit enforces even in CI - test: assert exact rendered shell string in substitution test; add workspace cwd test; add present-in-CI enforces test * fix(contracts): resolve OMN-7998 contract placeholder literals and pre-commit gate Replace {pr}/{repo} template literals with actual values (PR 588, OmniNode-ai/omnidash). Replace pre-commit runtime command with file_exists check for .pre-commit-config.yaml — pre-commit is not installed in CI runners. * fix(contracts): assert baseRefName==main in OMN-7998 dod-002 merged check dod-002 previously only checked mergedAt existed; CodeRabbit flagged it can pass on PRs not targeting main. Now asserts both non-empty mergedAt and baseRefName==main via awk, consistent with dod-001. * test: tighten pre-commit test assertions per CodeRabbit Minor - delenv CI in missing-not-ci test to pin non-CI path - assert exact detail string instead of loose "skipped" containment - capture _run calls in present-in-ci test to prove subprocess ran * fix(contracts): resolve dod-002 blocking on open PRs and {pr}/{repo} placeholder literals [OMN-7996, OMN-7998] OMN-7998: dod-002 used mergedAt check which always BLOCKs on open PRs since mergedAt is null. Changed to same OPEN|MERGED base-branch assertion as dod-001. OMN-7996: dod-001 and dod-002 had {pr}/{repo} placeholder literals never replaced with actual values. Substituted PR 586 / OmniNode-ai/omnidash. Changed dod-004 from pre-commit run (fails in CI) to file_exists check.
…ate → onex_change_control (#4271) * feat(OMN-14672): WS7 fan-out #4 — CI<->pre-commit byte-match parity gate for onex_change_control Ports the OMN-14655 canary (omniclaude#1904; models omnimarket#1783, omnibase_infra#2318): two meta-gates over .pre-commit-config.yaml wired as BOTH repo:local pre-commit hooks and an UNCONDITIONAL ci.yml job folded into the required CI Summary rollup. - src/onex_change_control/scripts/validate_precommit_fail_loud.py — exit-0-on-missing / WARN-SKIP-degrade + stages-coverage meta-gate (a skipped gate must be byte-indistinguishable from a failing one). - src/onex_change_control/scripts/validate_precommit_pin_parity.py — pin-parity ratchet: no-noncanonical-lifecycle-classes hook rev vs the ci.yml core SHA (63635097) for the same validator. - ci.yml: new unconditional precommit-parity-gate job (no needs/if) + ci-summary needs + strict success-only check, mirroring no-noncanonical-lifecycle-classes (OMN-14350). Standalone-workflow shape rejected (OMN-14441 removed one as invisible to the required rollup). - DRIFT-2a fix: default_install_hook_types [pre-commit, pre-push] -> [pre-commit, pre-push, commit-msg] (reject-deploy-gate-skip-token-commit-msg was never installed locally). Placed validators under src/onex_change_control/scripts/ (canonical OCC home per OMN-14475) to avoid the top-level scripts/ DEFAULT-DENY guard without allowlist gaming. In-branch DoD contract + receipts (OCC-native). SKIP=no-noncanonical-lifecycle-classes at commit: env-only local uv git-build cache gap (module exists at pinned SHA, gate green on live OCC CI PR#4269), hook untouched by this diff, still enforced in CI. * evidence(OMN-14672): bind in-branch DoD receipts to PR #4271
Implements OMN-962 - Complete schema models for drift control artifacts.
Deliverables
All enum files following naming conventions (
Enum*/enum_*.py):EnumDriftCategory- drift categories (scope, architecture, interfaces, dependencies, infra, process)EnumEvidenceKind- evidence types (tests, docs, ci, benchmark, manual)EnumInterfaceSurface- interface surfaces (events, topics, protocols, envelopes, public_api)EnumInvariantStatus- invariant check status (pass, fail, unknown)EnumPRState- PR states (merged, open)All model files following naming conventions (
Model*/model_*.py):ModelDayClose+ 7 supporting models (ProcessChange, PlanItem, PR, ActualRepo, DriftDetected, InvariantsChecked, Risk)ModelTicketContract+ 2 supporting models (EvidenceRequirement, EmergencyBypass)SemVer validation for
schema_versionfields using regex patternSchema purity: Models only import Pydantic and local enums (no runtime/infra coupling)
97% test coverage (12 tests, all passing)
Models validate existing drift reports (tested with 2025-12-20.yaml)
Acceptance Criteria
✅ Unit tests cover core structural validation
✅ Models are schema-pure (no runtime/infra coupling)
✅ Naming conventions aligned with
omnibase_core✅
schema_versionvalidated as SemVerTesting
Refs: OMN-962
Summary by CodeRabbit
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.