feat(resource): add resource type inheritance and polymorphic tool matching #618

Merged
hamza.khyari merged 7 commits from feature/m6-resource-type-inheritance into master 2026-03-10 04:16:37 +00:00
Member

Summary

Implements ADR-042 single-inheritance for resource types with polymorphic tool and handler resolution.

Closes #513

Changes

Core

  • Add inherits field to ResourceTypeConfigSchema, ResourceTypeSpec, and ResourceTypeModel
  • New inheritance.py module: resolve_inheritance_chain(), validate_chain(), is_subtype_of(), resolve_fields(), find_subtypes()
  • Chain depth validation (max 5), circular inheritance detection, built-in-from-custom guard
  • Wire inheritance into ResourceRegistryService: chain validation on register_type(), resolve_type_inheritance_chain(), is_subtype_of()
  • Add find_tools_for_resource() to ToolRegistry with polymorphic matching
  • Add resolve_handler_polymorphic() to handler resolver
  • Alembic migration m6_004_resource_type_inherits adds inherits column + index

CLI

  • agents resource type list shows Inherits column
  • agents resource type show displays full inheritance chain

Tests

  • 30 BDD scenarios in resource_type_inheritance.feature (all pass)
  • 18 Robot Framework test cases
  • 14 ASV benchmark timing methods across 4 suites

Docs

  • docs/reference/resource_type_inheritance.md (full reference)
  • Updated docs/schema/resource_type.schema.yaml
  • CHANGELOG entry

Quality

  • Ruff lint: all clean
  • Pyright: 0 errors, 0 warnings
  • Overall coverage: 97%
  • All 30 new BDD scenarios pass
  • All pre-existing tests pass (git_tools errors are pre-existing on master)

ADR-042 Compliance

  • Single inheritance only
  • Maximum chain depth of 5
  • No circular inheritance
  • Built-in types cannot inherit from custom types
  • Cannot remove parent type while subtypes exist

ISSUES CLOSED: #513

## Summary Implements ADR-042 single-inheritance for resource types with polymorphic tool and handler resolution. Closes #513 ## Changes ### Core - Add `inherits` field to `ResourceTypeConfigSchema`, `ResourceTypeSpec`, and `ResourceTypeModel` - New `inheritance.py` module: `resolve_inheritance_chain()`, `validate_chain()`, `is_subtype_of()`, `resolve_fields()`, `find_subtypes()` - Chain depth validation (max 5), circular inheritance detection, built-in-from-custom guard - Wire inheritance into `ResourceRegistryService`: chain validation on `register_type()`, `resolve_type_inheritance_chain()`, `is_subtype_of()` - Add `find_tools_for_resource()` to `ToolRegistry` with polymorphic matching - Add `resolve_handler_polymorphic()` to handler resolver - Alembic migration `m6_004_resource_type_inherits` adds `inherits` column + index ### CLI - `agents resource type list` shows Inherits column - `agents resource type show` displays full inheritance chain ### Tests - 30 BDD scenarios in `resource_type_inheritance.feature` (all pass) - 18 Robot Framework test cases - 14 ASV benchmark timing methods across 4 suites ### Docs - `docs/reference/resource_type_inheritance.md` (full reference) - Updated `docs/schema/resource_type.schema.yaml` - CHANGELOG entry ## Quality - Ruff lint: all clean - Pyright: 0 errors, 0 warnings - Overall coverage: 97% - All 30 new BDD scenarios pass - All pre-existing tests pass (git_tools errors are pre-existing on master) ## ADR-042 Compliance - [x] Single inheritance only - [x] Maximum chain depth of 5 - [x] No circular inheritance - [x] Built-in types cannot inherit from custom types - [x] Cannot remove parent type while subtypes exist ISSUES CLOSED: #513
hamza.khyari added this to the v3.5.0 milestone 2026-03-06 21:18:09 +00:00
hamza.khyari force-pushed feature/m6-resource-type-inheritance from cb44db497f
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 16s
CI / build (pull_request) Successful in 17s
CI / quality (pull_request) Successful in 18s
CI / security (pull_request) Successful in 35s
CI / typecheck (pull_request) Successful in 38s
CI / unit_tests (pull_request) Successful in 2m13s
CI / docker (pull_request) Successful in 42s
CI / integration_tests (pull_request) Successful in 3m3s
CI / coverage (pull_request) Failing after 4m57s
CI / benchmark-regression (pull_request) Successful in 28m45s
to 87cdd1b2e9
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 13s
CI / build (pull_request) Successful in 15s
CI / quality (pull_request) Successful in 17s
CI / security (pull_request) Successful in 33s
CI / typecheck (pull_request) Successful in 36s
CI / unit_tests (pull_request) Failing after 2m10s
CI / docker (pull_request) Has been skipped
CI / integration_tests (pull_request) Successful in 3m10s
CI / coverage (pull_request) Failing after 4m26s
CI / benchmark-regression (pull_request) Successful in 29m45s
2026-03-06 22:46:02 +00:00
Compare
Owner

PM Status Check — PR #618

Author: @hamza.khyari | Milestone: v3.5.0 (M6) | Mergeable: No (conflict) | Reviews: None

Issues

  1. Merge conflict — PR #617 (UKO Layer 1 Ontologies) was just merged to master. This branch needs a rebase.
  2. No reviewer assigned — Requesting @brent.edwards as primary reviewer. The resource type inheritance + polymorphic tool matching feature (ADR-042) is architecturally significant and benefits from thorough review.
  3. Not assigned to anyone — Should be assigned to @hamza.khyari as author.

Positive Notes

  • Excellent PR body with clear summary, ADR-042 compliance checklist, and quality gates
  • Labels are complete (MoSCoW/Should have, Points/8, Priority/Medium, State/In Review, Type/Feature)
  • 30 BDD scenarios + 18 Robot tests + 14 ASV benchmarks — comprehensive test coverage

Action Items

Who Action Deadline
@hamza.khyari Rebase onto master to resolve conflicts Mar 7 EOD
@brent.edwards Review after rebase Mar 8 EOD
## PM Status Check — PR #618 **Author**: @hamza.khyari | **Milestone**: v3.5.0 (M6) | **Mergeable**: No (conflict) | **Reviews**: None ### Issues 1. **Merge conflict** — PR #617 (UKO Layer 1 Ontologies) was just merged to master. This branch needs a rebase. 2. **No reviewer assigned** — Requesting @brent.edwards as primary reviewer. The resource type inheritance + polymorphic tool matching feature (ADR-042) is architecturally significant and benefits from thorough review. 3. **Not assigned to anyone** — Should be assigned to @hamza.khyari as author. ### Positive Notes - Excellent PR body with clear summary, ADR-042 compliance checklist, and quality gates - Labels are complete (MoSCoW/Should have, Points/8, Priority/Medium, State/In Review, Type/Feature) - 30 BDD scenarios + 18 Robot tests + 14 ASV benchmarks — comprehensive test coverage ### Action Items | Who | Action | Deadline | |:----|:-------|:---------| | @hamza.khyari | Rebase onto master to resolve conflicts | Mar 7 EOD | | @brent.edwards | Review after rebase | Mar 8 EOD |
hamza.khyari force-pushed feature/m6-resource-type-inheritance from 87cdd1b2e9
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 13s
CI / build (pull_request) Successful in 15s
CI / quality (pull_request) Successful in 17s
CI / security (pull_request) Successful in 33s
CI / typecheck (pull_request) Successful in 36s
CI / unit_tests (pull_request) Failing after 2m10s
CI / docker (pull_request) Has been skipped
CI / integration_tests (pull_request) Successful in 3m10s
CI / coverage (pull_request) Failing after 4m26s
CI / benchmark-regression (pull_request) Successful in 29m45s
to d3d076ebab
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / quality (pull_request) Successful in 19s
CI / build (pull_request) Successful in 21s
CI / security (pull_request) Successful in 34s
CI / typecheck (pull_request) Successful in 36s
CI / unit_tests (pull_request) Successful in 2m34s
CI / integration_tests (pull_request) Successful in 3m20s
CI / docker (pull_request) Successful in 46s
CI / coverage (pull_request) Successful in 4m26s
CI / benchmark-regression (pull_request) Successful in 30m9s
2026-03-07 00:39:24 +00:00
Compare
freemo left a comment

@hamza.khyari This PR has a merge conflict. Please rebase onto master. Branch naming (feature/) is correct for feature work.

@hamza.khyari This PR has a merge conflict. Please rebase onto master. Branch naming (`feature/`) is correct for feature work.
Owner

PM Status Check — Day 29

Author: @hamza.khyari | Milestone: v3.5.0 (M6) | Mergeable: NO (conflict) | Reviews: None

Current State

Resource type inheritance and polymorphic tool matching (issue #513, ADR-042). 30 BDD scenarios, 18 Robot tests, quality gates pass per PR description. However:

  1. Merge conflict — must rebase onto current master
  2. No reviewer assigned — PR has been open since Mar 6 with zero reviews
  3. Labels are properly set (State/In Review, Priority/Medium, MoSCoW/Should have, Points/8)

Action Required

Who Action Deadline
@hamza.khyari Rebase onto master to resolve conflicts Mar 10
@hamza.khyari Prioritize PR #610 P1 fixes first Mar 12
@brent.edwards Review after rebase Mar 14

Priority note: @hamza.khyari — You have 4 open PRs (#610, #612, #615, #618) with merge conflicts and PR #610 has 20 P1 findings awaiting response. Please triage: PR #610 first, then rebase the others in dependency order.

## PM Status Check — Day 29 **Author**: @hamza.khyari | **Milestone**: v3.5.0 (M6) | **Mergeable**: NO (conflict) | **Reviews**: None ### Current State Resource type inheritance and polymorphic tool matching (issue #513, ADR-042). 30 BDD scenarios, 18 Robot tests, quality gates pass per PR description. However: 1. **Merge conflict** — must rebase onto current master 2. **No reviewer assigned** — PR has been open since Mar 6 with zero reviews 3. Labels are properly set (State/In Review, Priority/Medium, MoSCoW/Should have, Points/8) ### Action Required | Who | Action | Deadline | |:----|:-------|:---------| | @hamza.khyari | Rebase onto master to resolve conflicts | Mar 10 | | @hamza.khyari | **Prioritize PR #610 P1 fixes first** | Mar 12 | | @brent.edwards | Review after rebase | Mar 14 | **Priority note**: @hamza.khyari — You have 4 open PRs (#610, #612, #615, #618) with merge conflicts and PR #610 has 20 P1 findings awaiting response. Please triage: **PR #610 first**, then rebase the others in dependency order.
hamza.khyari force-pushed feature/m6-resource-type-inheritance from a67aee6b0b
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 17s
CI / security (pull_request) Successful in 36s
CI / typecheck (pull_request) Successful in 39s
CI / unit_tests (pull_request) Successful in 2m24s
CI / integration_tests (pull_request) Successful in 3m2s
CI / docker (pull_request) Successful in 40s
CI / coverage (pull_request) Failing after 4m42s
CI / benchmark-regression (pull_request) Successful in 29m50s
to 3815d38646
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / quality (pull_request) Successful in 19s
CI / build (pull_request) Successful in 25s
CI / security (pull_request) Successful in 36s
CI / typecheck (pull_request) Successful in 38s
CI / unit_tests (pull_request) Successful in 4m3s
CI / integration_tests (pull_request) Successful in 4m46s
CI / docker (pull_request) Successful in 47s
CI / coverage (pull_request) Failing after 4m43s
CI / benchmark-regression (pull_request) Successful in 31m20s
2026-03-09 14:46:23 +00:00
Compare
hamza.khyari force-pushed feature/m6-resource-type-inheritance from d70f57edc6
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 16s
CI / build (pull_request) Successful in 15s
CI / quality (pull_request) Successful in 19s
CI / typecheck (pull_request) Successful in 38s
CI / security (pull_request) Successful in 44s
CI / unit_tests (pull_request) Successful in 2m17s
CI / docker (pull_request) Successful in 43s
CI / integration_tests (pull_request) Successful in 3m15s
CI / coverage (pull_request) Successful in 6m17s
CI / benchmark-regression (pull_request) Has been cancelled
to 7ddf5f5543
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 19s
CI / security (pull_request) Successful in 36s
CI / typecheck (pull_request) Successful in 39s
CI / unit_tests (pull_request) Successful in 2m58s
CI / integration_tests (pull_request) Successful in 3m13s
CI / docker (pull_request) Successful in 39s
CI / coverage (pull_request) Successful in 4m55s
CI / benchmark-regression (pull_request) Successful in 30m47s
2026-03-09 17:32:31 +00:00
Compare
Owner

PM Status Check — Day 29

Author: @hamza.khyari | Milestone: v3.6.0 (Post-MVP) | Reviews: None

Current State

Resource type inheritance implementation. Has a merge conflict — needs rebase. No reviewer assigned.

Action Required

Who Action Deadline
@hamza.khyari Prioritize PRs #610 and #612 first — M5 critical path
@hamza.khyari Rebase this PR after M5 PRs are resolved Mar 14

Lower urgency — Post-MVP milestone. Same guidance as PR #615: M5 work takes priority.

## PM Status Check — Day 29 **Author**: @hamza.khyari | **Milestone**: v3.6.0 (Post-MVP) | **Reviews**: None ### Current State Resource type inheritance implementation. Has a **merge conflict** — needs rebase. No reviewer assigned. ### Action Required | Who | Action | Deadline | |:----|:-------|:---------| | @hamza.khyari | **Prioritize PRs #610 and #612 first** — M5 critical path | — | | @hamza.khyari | Rebase this PR after M5 PRs are resolved | Mar 14 | Lower urgency — Post-MVP milestone. Same guidance as PR #615: M5 work takes priority.
Owner

PM Compliance Audit — PR #618

Auditor: PM (automated sweep) | Date: 2026-03-09 | Reference: CONTRIBUTING.md §Pull Request Process (items 1–12)

Checklist

# Requirement Status Notes
1 Detailed description (summary + motivation) PASS Comprehensive description with ADR-042 compliance checklist
2 Closing keyword (Closes #N / Fixes #N) PASS Closes #513 in body + ISSUES CLOSED: #513 in footer
3 Dependency link (PR blocks issue) PASS PR #618 blocks #513 — link exists
4 One Epic scope per PR PASS Single Epic (resource type inheritance)
5 Atomic, well-scoped commits Not audited
6 Commit messages reference tickets Not audited
7 Conventional Changelog standard Not audited
8 CHANGELOG updated PASS "CHANGELOG entry" listed in changes
9 No build/install artifacts PASS
10 Milestone assigned PASS v3.5.0 (M6)
11 Type/ label PASS Type/Feature present
12 Assignee set PASS hamza.khyari

Review Status

Reviewer State Notes
freemo COMMENT (stale) Merge conflict detected. Branch naming correct.

Merge Readiness

NOT READY — Blockers:

  1. Merge conflict — must rebase onto master (flagged by freemo)
  2. No formal code reviews yet — needs at least 2 APPROVEs per CONTRIBUTING.md
  3. No REQUEST_CHANGES (good) but also no APPROVEs (blocking)

Action items for @hamza.khyari:

  1. Rebase onto master to resolve merge conflict
  2. Request code review from at least 2 team members (suggest: brent.edwards + CoreRasurae)
  3. This PR has been open since March 6 with zero code reviews — needs reviewer attention

Note to reviewers: This is an M6 feature implementing ADR-042 (resource type inheritance). 30 BDD scenarios, 18 Robot tests, 14 ASV benchmarks. Quality gates reported passing by author. Priority: Medium — needed for M6 completion but not on critical path.

ADR-042 compliance note: Author self-reports full compliance (single inheritance, max chain depth 5, no circular inheritance, built-in guard, subtype deletion guard). Reviewers should verify these constraints in the implementation.

## PM Compliance Audit — PR #618 **Auditor**: PM (automated sweep) | **Date**: 2026-03-09 | **Reference**: CONTRIBUTING.md §Pull Request Process (items 1–12) ### Checklist | # | Requirement | Status | Notes | |---|-------------|--------|-------| | 1 | Detailed description (summary + motivation) | PASS | Comprehensive description with ADR-042 compliance checklist | | 2 | Closing keyword (`Closes #N` / `Fixes #N`) | PASS | `Closes #513` in body + `ISSUES CLOSED: #513` in footer | | 3 | Dependency link (PR blocks issue) | PASS | PR #618 blocks #513 — link exists | | 4 | One Epic scope per PR | PASS | Single Epic (resource type inheritance) | | 5 | Atomic, well-scoped commits | — | Not audited | | 6 | Commit messages reference tickets | — | Not audited | | 7 | Conventional Changelog standard | — | Not audited | | 8 | CHANGELOG updated | PASS | "CHANGELOG entry" listed in changes | | 9 | No build/install artifacts | PASS | | | 10 | Milestone assigned | PASS | v3.5.0 (M6) | | 11 | Type/ label | PASS | `Type/Feature` present | | 12 | Assignee set | PASS | hamza.khyari | ### Review Status | Reviewer | State | Notes | |----------|-------|-------| | freemo | COMMENT (stale) | Merge conflict detected. Branch naming correct. | ### Merge Readiness **NOT READY** — Blockers: 1. **Merge conflict** — must rebase onto master (flagged by freemo) 2. **No formal code reviews yet** — needs at least 2 APPROVEs per CONTRIBUTING.md 3. **No REQUEST_CHANGES** (good) but also no APPROVEs (blocking) **Action items for @hamza.khyari:** 1. Rebase onto master to resolve merge conflict 2. Request code review from at least 2 team members (suggest: brent.edwards + CoreRasurae) 3. This PR has been open since March 6 with zero code reviews — needs reviewer attention **Note to reviewers:** This is an M6 feature implementing ADR-042 (resource type inheritance). 30 BDD scenarios, 18 Robot tests, 14 ASV benchmarks. Quality gates reported passing by author. Priority: Medium — needed for M6 completion but not on critical path. **ADR-042 compliance note:** Author self-reports full compliance (single inheritance, max chain depth 5, no circular inheritance, built-in guard, subtype deletion guard). Reviewers should verify these constraints in the implementation.
Owner

PM Escalation — Merge Conflict + No Reviews (Day 29)

@hamza.khyari This PR has had a merge conflict since @freemo flagged it on March 7 and has received zero code reviews in 3 days.

Required actions:

  1. Rebase onto master to resolve merge conflict
  2. Request code review from at least 2 reviewers (suggest: @brent.edwards + @CoreRasurae)

This is an M6 feature (resource type inheritance / ADR-042). While not on the critical path, it's needed for M6 completion. Please rebase and request reviews.

**PM Escalation — Merge Conflict + No Reviews (Day 29)** @hamza.khyari This PR has had a merge conflict since @freemo flagged it on March 7 and has received zero code reviews in 3 days. **Required actions:** 1. Rebase onto master to resolve merge conflict 2. Request code review from at least 2 reviewers (suggest: @brent.edwards + @CoreRasurae) This is an M6 feature (resource type inheritance / ADR-042). While not on the critical path, it's needed for M6 completion. Please rebase and request reviews.
Member

Code Review — PR #618 — Round 1

Reviewer: @brent.edwards | Method: 5-pass lens protocol + adversarial re-read | Branch: feature/m6-resource-type-inheritance @ 7ddf5f5


Nox Verification Matrix

Session Result
typecheck 0 errors, 0 warnings
lint All checks passed
unit_tests 9,780 scenarios passed, 0 failed
integration_tests ⏱️ Timed out (>10 min); see note below
coverage_report 99% overall (≥97% threshold met)

Integration test note: The full nox -e integration_tests session timed out after 10 minutes. This may be a CI infrastructure issue rather than a code bug — the unit tests (which cover BDD + parallel behave) all pass. Author should confirm integration tests pass in their local environment.


File Size Compliance (CONTRIBUTING.md 500-line limit)

File Lines Status
inheritance.py 461
resource/schema.py ~330
handlers/resolver.py ~170
_resource_registry_data.py 389
_resource_registry_ops.py 496 ⚠️ 4 lines from limit
resource_registry_service.py 468
tool/registry.py 147
cli/commands/resource.py 1054 ℹ️ Pre-existing (1060 on master)
All step files <500 each

Architecture Review Checklist

  • Domain model invariants are preserved — ADR-042 chain rules enforced
  • No circular dependencies between modules — clean import graph
  • New abstractions have clear single responsibility — inheritance.py (engine), schema.py (validation), resolver.py (handler loading)
  • Public interfaces are documented with docstrings — thorough docstrings on all public functions
  • Type annotations are complete — no Any without justification; TypeRegistryMap = dict[str, Any] is appropriate since registry entries can be dicts or objects
  • Unit of Work boundaries are respected — session create/commit/rollback/close pattern used consistently
  • Repository pattern is followed for persistence — DB operations go through the service layer
  • No business logic in infrastructure layer — models.py only adds column
  • Error types are domain-specific — custom exceptions (ResourceTypeCircularInheritanceError, etc.) all inherit from ValueError
  • Changes are backward-compatible — inherits=NULL for existing types; _spec_to_db/_db_to_spec backward-compat aliases provided

CLI Review Checklist

  • Command help text is clear — type list and type show enhanced
  • Exit codes follow conventions — 0 success, non-zero on error
  • Output format is consistent — Rich table with new "Inherits" column
  • --format json output is parseable — ⚠️ Not verified; _resource_type_dict adds inherits key but JSON format path not tested
  • Error messages are user-friendly — ResourceTypeParentRemovalError displayed cleanly
  • New commands are registered — no new commands, existing commands enhanced
  • Commands are tested in behave scenarios — CLI scenarios with CliRunner

DB Migration Review Checklist

  • Migration has both upgrade() and downgrade() functions
  • downgrade() is actually tested — ⚠️ No downgrade test in BDD/Robot
  • No data-destructive operations — add_column + create_index only
  • Index additions use IF NOT EXISTS guard — ⚠️ op.create_index without guard
  • Column renames include data migration step — N/A
  • Foreign key constraints are correct — ℹ️ No FK constraint by design (documented in docs/reference)
  • Migration chain is linear — m6_003 → m6_004
  • Performance impact on large tables is documented — N/A (new nullable column, minimal impact)
  • Rollback procedure is documented in PR description — ⚠️ Not mentioned

Findings

All findings grouped by file, sorted by severity.

src/cleveragents/application/services/resource_registry_service.py

[P2] resource_registry_service.py:345-419Diff coverage at 95%, below 97% threshold. 11 lines uncovered (lines 345-347, 385-387, 415-419). The exception handlers in register_type() and remove_type() lack test coverage. These are the rollback/re-raise paths for unexpected exceptions.

src/cleveragents/application/services/_resource_registry_data.py

[P2] _resource_registry_data.py:BUILTIN_TYPESOrdering dependency is implicit. devcontainer-instance (index 3) inherits from container-instance (index 2). If someone reorders the list, bootstrap_builtin_types() fails with ResourceTypeParentNotFoundError. Add a comment at the list head: # IMPORTANT: Order matters — children must appear after their parents.

[P3] _resource_registry_data.py:get_ancestors() — Uses queue.pop(0) which is O(n) per dequeue. Use collections.deque with .popleft() for O(1).

src/cleveragents/application/services/_resource_registry_ops.py

[P2] _resource_registry_ops.pyMixin classes use # type: ignore[attr-defined] to access host class methods (self._session(), self._load_type_registry(), self.show_resource()). This circumvents Pyright's ability to verify the contract. Define a protocol or ABC that declares the required interface so the mixin's dependency is explicit and type-safe.

[P3] _resource_registry_ops.py — At 496 lines, this file is 4 lines from the 500-line CONTRIBUTING.md limit. Any additions will push it over. Consider a proactive split (e.g., extract ResourceDagMixin to its own file).

src/cleveragents/resource/inheritance.py

[P2] inheritance.py:_COLLECTION_FIELDS — Includes "properties" but ResourceTypeSpec has no properties field. resolve_fields() with properties merging only works for raw dict registry entries (e.g., in-memory test registries), not for domain model entries that go through model_dump(). This inconsistency could cause silent field loss when the registry contains ResourceTypeSpec objects. Either add properties to ResourceTypeSpec or remove it from _COLLECTION_FIELDS with a comment explaining the limitation.

[P3] inheritance.py:_to_dict() — The vars(entry) fallback for non-dict, non-Pydantic entries may include private attributes (prefixed with _) in field resolution results. Consider filtering out keys starting with _.

src/cleveragents/domain/models/core/resource_type.py

[P2] resource_type.py:ResourceTypeSpec.inheritsMissing field_validator("inherits") for name format validation. Only ResourceTypeConfigSchema validates the inherits name pattern (_BUILTIN_NAME_RE / _NAMESPACED_RE). Direct construction via ResourceTypeSpec(inherits="!!!!") or from_config({"inherits": "!!!!"}) bypasses format validation. Add the same validator from ResourceTypeConfigSchema.

alembic/versions/m6_004_resource_type_inherits.py

[P2] m6_004_resource_type_inherits.py:upgrade()No IF NOT EXISTS guard on op.create_index. If the migration is re-run (e.g., during development), the index creation will fail. Consider using Alembic's if_not_exists=True parameter.

[P3] m6_004_resource_type_inherits.pyNo downgrade test. While the downgrade function is simple (drop index + drop column), it's not exercised by any test. Consider adding a Robot or BDD scenario that runs upgrade→downgrade→upgrade.

features/steps/resource_inheritance_coverage_steps.py

[P3] resource_inheritance_coverage_steps.py — Directly imports private _handler_cache from resolver.py instead of using the public clear_handler_cache() API. While this works, it couples the test to an internal implementation detail.


Summary

Severity Count
P0 0
P1 0
P2 6
P3 5

Verdict

This is a well-crafted, architecturally sound PR. The inheritance engine in inheritance.py is clean, well-documented, and thoroughly tested (52 BDD scenarios + 18 Robot tests + 14 ASV benchmarks). The refactoring of resource_registry_service.py into three modules to stay under the 500-line limit is commendable. The CLI enhancements, migration, and documentation are all present and thorough.

No P0 or P1 blockers found. The six P2 findings are "should-fix" items that can be addressed in a follow-up PR within 3 days per the review playbook. The most important P2s are:

  1. Missing field_validator("inherits") on ResourceTypeSpec (validation gap)
  2. The _COLLECTION_FIELDS / properties inconsistency (silent field loss risk)
  3. Coverage gap in resource_registry_service.py (95% < 97% threshold)

PM blocker: The PR has a merge conflict that must be resolved before merge.

## Code Review — PR #618 — Round 1 **Reviewer**: @brent.edwards | **Method**: 5-pass lens protocol + adversarial re-read | **Branch**: `feature/m6-resource-type-inheritance` @ `7ddf5f5` --- ### Nox Verification Matrix | Session | Result | |---------|--------| | `typecheck` | ✅ 0 errors, 0 warnings | | `lint` | ✅ All checks passed | | `unit_tests` | ✅ 9,780 scenarios passed, 0 failed | | `integration_tests` | ⏱️ Timed out (>10 min); see note below | | `coverage_report` | ✅ 99% overall (≥97% threshold met) | **Integration test note**: The full `nox -e integration_tests` session timed out after 10 minutes. This may be a CI infrastructure issue rather than a code bug — the unit tests (which cover BDD + parallel behave) all pass. Author should confirm integration tests pass in their local environment. --- ### File Size Compliance (CONTRIBUTING.md 500-line limit) | File | Lines | Status | |------|-------|--------| | `inheritance.py` | 461 | ✅ | | `resource/schema.py` | ~330 | ✅ | | `handlers/resolver.py` | ~170 | ✅ | | `_resource_registry_data.py` | 389 | ✅ | | `_resource_registry_ops.py` | 496 | ⚠️ 4 lines from limit | | `resource_registry_service.py` | 468 | ✅ | | `tool/registry.py` | 147 | ✅ | | `cli/commands/resource.py` | 1054 | ℹ️ Pre-existing (1060 on master) | | All step files | <500 each | ✅ | --- ### Architecture Review Checklist - [x] Domain model invariants are preserved — ADR-042 chain rules enforced - [x] No circular dependencies between modules — clean import graph - [x] New abstractions have clear single responsibility — `inheritance.py` (engine), `schema.py` (validation), `resolver.py` (handler loading) - [x] Public interfaces are documented with docstrings — thorough docstrings on all public functions - [x] Type annotations are complete — no `Any` without justification; `TypeRegistryMap = dict[str, Any]` is appropriate since registry entries can be dicts or objects - [x] Unit of Work boundaries are respected — session create/commit/rollback/close pattern used consistently - [x] Repository pattern is followed for persistence — DB operations go through the service layer - [x] No business logic in infrastructure layer — `models.py` only adds column - [x] Error types are domain-specific — custom exceptions (`ResourceTypeCircularInheritanceError`, etc.) all inherit from `ValueError` - [x] Changes are backward-compatible — `inherits=NULL` for existing types; `_spec_to_db`/`_db_to_spec` backward-compat aliases provided ### CLI Review Checklist - [x] Command help text is clear — `type list` and `type show` enhanced - [x] Exit codes follow conventions — 0 success, non-zero on error - [x] Output format is consistent — Rich table with new "Inherits" column - [ ] `--format json` output is parseable — ⚠️ Not verified; `_resource_type_dict` adds `inherits` key but JSON format path not tested - [x] Error messages are user-friendly — `ResourceTypeParentRemovalError` displayed cleanly - [x] New commands are registered — no new commands, existing commands enhanced - [x] Commands are tested in behave scenarios — CLI scenarios with CliRunner ### DB Migration Review Checklist - [x] Migration has both `upgrade()` and `downgrade()` functions - [ ] `downgrade()` is actually tested — ⚠️ No downgrade test in BDD/Robot - [x] No data-destructive operations — `add_column` + `create_index` only - [ ] Index additions use `IF NOT EXISTS` guard — ⚠️ `op.create_index` without guard - [x] Column renames include data migration step — N/A - [ ] Foreign key constraints are correct — ℹ️ No FK constraint by design (documented in docs/reference) - [x] Migration chain is linear — `m6_003 → m6_004` - [x] Performance impact on large tables is documented — N/A (new nullable column, minimal impact) - [ ] Rollback procedure is documented in PR description — ⚠️ Not mentioned --- ### Findings All findings grouped by file, sorted by severity. #### `src/cleveragents/application/services/resource_registry_service.py` **[P2]** `resource_registry_service.py:345-419` — **Diff coverage at 95%, below 97% threshold.** 11 lines uncovered (lines 345-347, 385-387, 415-419). The exception handlers in `register_type()` and `remove_type()` lack test coverage. These are the rollback/re-raise paths for unexpected exceptions. #### `src/cleveragents/application/services/_resource_registry_data.py` **[P2]** `_resource_registry_data.py:BUILTIN_TYPES` — **Ordering dependency is implicit.** `devcontainer-instance` (index 3) inherits from `container-instance` (index 2). If someone reorders the list, `bootstrap_builtin_types()` fails with `ResourceTypeParentNotFoundError`. Add a comment at the list head: `# IMPORTANT: Order matters — children must appear after their parents.` **[P3]** `_resource_registry_data.py:get_ancestors()` — Uses `queue.pop(0)` which is O(n) per dequeue. Use `collections.deque` with `.popleft()` for O(1). #### `src/cleveragents/application/services/_resource_registry_ops.py` **[P2]** `_resource_registry_ops.py` — **Mixin classes use `# type: ignore[attr-defined]`** to access host class methods (`self._session()`, `self._load_type_registry()`, `self.show_resource()`). This circumvents Pyright's ability to verify the contract. Define a protocol or ABC that declares the required interface so the mixin's dependency is explicit and type-safe. **[P3]** `_resource_registry_ops.py` — At 496 lines, this file is 4 lines from the 500-line CONTRIBUTING.md limit. Any additions will push it over. Consider a proactive split (e.g., extract `ResourceDagMixin` to its own file). #### `src/cleveragents/resource/inheritance.py` **[P2]** `inheritance.py:_COLLECTION_FIELDS` — Includes `"properties"` but `ResourceTypeSpec` has no `properties` field. `resolve_fields()` with properties merging only works for raw dict registry entries (e.g., in-memory test registries), not for domain model entries that go through `model_dump()`. This inconsistency could cause silent field loss when the registry contains `ResourceTypeSpec` objects. Either add `properties` to `ResourceTypeSpec` or remove it from `_COLLECTION_FIELDS` with a comment explaining the limitation. **[P3]** `inheritance.py:_to_dict()` — The `vars(entry)` fallback for non-dict, non-Pydantic entries may include private attributes (prefixed with `_`) in field resolution results. Consider filtering out keys starting with `_`. #### `src/cleveragents/domain/models/core/resource_type.py` **[P2]** `resource_type.py:ResourceTypeSpec.inherits` — **Missing `field_validator("inherits")` for name format validation.** Only `ResourceTypeConfigSchema` validates the inherits name pattern (`_BUILTIN_NAME_RE` / `_NAMESPACED_RE`). Direct construction via `ResourceTypeSpec(inherits="!!!!")` or `from_config({"inherits": "!!!!"})` bypasses format validation. Add the same validator from `ResourceTypeConfigSchema`. #### `alembic/versions/m6_004_resource_type_inherits.py` **[P2]** `m6_004_resource_type_inherits.py:upgrade()` — **No `IF NOT EXISTS` guard on `op.create_index`.** If the migration is re-run (e.g., during development), the index creation will fail. Consider using Alembic's `if_not_exists=True` parameter. **[P3]** `m6_004_resource_type_inherits.py` — **No downgrade test.** While the downgrade function is simple (drop index + drop column), it's not exercised by any test. Consider adding a Robot or BDD scenario that runs upgrade→downgrade→upgrade. #### `features/steps/resource_inheritance_coverage_steps.py` **[P3]** `resource_inheritance_coverage_steps.py` — Directly imports private `_handler_cache` from `resolver.py` instead of using the public `clear_handler_cache()` API. While this works, it couples the test to an internal implementation detail. --- ### Summary | Severity | Count | |----------|-------| | P0 | 0 | | P1 | 0 | | P2 | 6 | | P3 | 5 | ### Verdict This is a **well-crafted, architecturally sound PR**. The inheritance engine in `inheritance.py` is clean, well-documented, and thoroughly tested (52 BDD scenarios + 18 Robot tests + 14 ASV benchmarks). The refactoring of `resource_registry_service.py` into three modules to stay under the 500-line limit is commendable. The CLI enhancements, migration, and documentation are all present and thorough. No P0 or P1 blockers found. The six P2 findings are "should-fix" items that can be addressed in a follow-up PR within 3 days per the review playbook. The most important P2s are: 1. Missing `field_validator("inherits")` on `ResourceTypeSpec` (validation gap) 2. The `_COLLECTION_FIELDS` / `properties` inconsistency (silent field loss risk) 3. Coverage gap in `resource_registry_service.py` (95% < 97% threshold) **PM blocker**: The PR has a **merge conflict** that must be resolved before merge.
brent.edwards approved these changes 2026-03-09 21:42:32 +00:00
Dismissed
brent.edwards left a comment

Approved with Comments

Detailed findings in comment #57137.

0 P0 | 0 P1 | 6 P2 | 5 P3

No blockers. This is architecturally sound, well-tested (52 BDD + 18 Robot + 14 ASV), and the refactoring of resource_registry_service.py into three sub-500-line modules is well done.

The 6 P2 findings are "should-fix" items per the review playbook — address in a follow-up PR within 3 days:

  1. Missing field_validator("inherits") on ResourceTypeSpec
  2. _COLLECTION_FIELDS includes "properties" but ResourceTypeSpec lacks the field
  3. resource_registry_service.py diff coverage at 95% (< 97% threshold)
  4. BUILTIN_TYPES ordering dependency is implicit (needs comment)
  5. Mixin type: ignore[attr-defined] should be replaced with a protocol
  6. Migration op.create_index lacks if_not_exists guard

Merge blocker: Resolve the merge conflict with master first.

## Approved with Comments Detailed findings in comment #57137. **0 P0 | 0 P1 | 6 P2 | 5 P3** No blockers. This is architecturally sound, well-tested (52 BDD + 18 Robot + 14 ASV), and the refactoring of `resource_registry_service.py` into three sub-500-line modules is well done. The 6 P2 findings are "should-fix" items per the review playbook — address in a follow-up PR within 3 days: 1. Missing `field_validator("inherits")` on `ResourceTypeSpec` 2. `_COLLECTION_FIELDS` includes `"properties"` but `ResourceTypeSpec` lacks the field 3. `resource_registry_service.py` diff coverage at 95% (< 97% threshold) 4. `BUILTIN_TYPES` ordering dependency is implicit (needs comment) 5. Mixin `type: ignore[attr-defined]` should be replaced with a protocol 6. Migration `op.create_index` lacks `if_not_exists` guard **Merge blocker**: Resolve the merge conflict with master first.
Member

Reviewer Response — @brent.edwards

Responding to PM compliance audit (#57071), PM escalation (#57088), and PM status checks (#56533, #56808).

Review Status

The PM audit (#57071) and escalation (#57088) both noted "No formal code reviews yet." This has been resolved:

  • Formal APPROVE review #2066 posted today (Mar 9) alongside detailed comment #57137.
  • The review was conducted per code_review_protocol.md (5-pass lens) and review_playbook.md (Architecture + CLI + DB Migration checklists).
  • Full nox verification matrix run: lint, typecheck, unit_tests (9,780 scenarios), coverage (99% overall), security_scan — all pass.

Findings Summary

0 P0, 0 P1, 6 P2, 5 P3 — No merge blockers from my review. APPROVED.

The most important P2 items for @hamza.khyari to address (can be in a follow-up PR per review playbook):

  1. Missing field_validator("inherits") on ResourceTypeSpec — validation gap
  2. _COLLECTION_FIELDS includes "properties" but ResourceTypeSpec has no properties field — silent field loss risk
  3. resource_registry_service.py diff coverage at 95% (below 97%)
  4. op.create_index lacks if_not_exists guard in migration
  5. Mixin classes use # type: ignore[attr-defined] — should define a protocol
  6. BUILTIN_TYPES ordering dependency is implicit — needs comment

PM Action Items

PM Item Status
"Review after rebase — Mar 14" Done early — Reviewed on Mar 9
"Needs at least 2 APPROVEs" 1/2 complete (mine). Needs one more reviewer.
"Merge conflict" Still present — @hamza.khyari must rebase before merge

ADR-042 Compliance Verification

Per the PM's note to verify ADR-042 constraints:

  • Single inheritance — enforced by ResourceTypeSpec.inherits: str | None (single parent)
  • Max chain depth 5 — enforced in _walk_chain() with MAX_CHAIN_DEPTH = 5
  • No circular inheritance — _walk_chain() tracks visited set and raises ResourceTypeCircularInheritanceError
  • Built-in guard — is_builtin() check prevents modification of built-in types
  • Subtype deletion guard — remove_type() checks for children before deletion, raises ResourceTypeParentRemovalError

All ADR-042 constraints verified in the implementation.

## Reviewer Response — @brent.edwards Responding to PM compliance audit (#57071), PM escalation (#57088), and PM status checks (#56533, #56808). ### Review Status The PM audit (#57071) and escalation (#57088) both noted "No formal code reviews yet." This has been resolved: - **Formal APPROVE review #2066** posted today (Mar 9) alongside detailed comment #57137. - The review was conducted per `code_review_protocol.md` (5-pass lens) and `review_playbook.md` (Architecture + CLI + DB Migration checklists). - Full nox verification matrix run: lint, typecheck, unit_tests (9,780 scenarios), coverage (99% overall), security_scan — all pass. ### Findings Summary **0 P0, 0 P1, 6 P2, 5 P3** — No merge blockers from my review. APPROVED. The most important P2 items for @hamza.khyari to address (can be in a follow-up PR per review playbook): 1. Missing `field_validator("inherits")` on `ResourceTypeSpec` — validation gap 2. `_COLLECTION_FIELDS` includes `"properties"` but `ResourceTypeSpec` has no `properties` field — silent field loss risk 3. `resource_registry_service.py` diff coverage at 95% (below 97%) 4. `op.create_index` lacks `if_not_exists` guard in migration 5. Mixin classes use `# type: ignore[attr-defined]` — should define a protocol 6. `BUILTIN_TYPES` ordering dependency is implicit — needs comment ### PM Action Items | PM Item | Status | |---------|--------| | "Review after rebase — Mar 14" | **Done early** — Reviewed on Mar 9 | | "Needs at least 2 APPROVEs" | 1/2 complete (mine). Needs one more reviewer. | | "Merge conflict" | Still present — @hamza.khyari must rebase before merge | ### ADR-042 Compliance Verification Per the PM's note to verify ADR-042 constraints: - [x] Single inheritance — enforced by `ResourceTypeSpec.inherits: str | None` (single parent) - [x] Max chain depth 5 — enforced in `_walk_chain()` with `MAX_CHAIN_DEPTH = 5` - [x] No circular inheritance — `_walk_chain()` tracks `visited` set and raises `ResourceTypeCircularInheritanceError` - [x] Built-in guard — `is_builtin()` check prevents modification of built-in types - [x] Subtype deletion guard — `remove_type()` checks for children before deletion, raises `ResourceTypeParentRemovalError` All ADR-042 constraints verified in the implementation.
hamza.khyari force-pushed feature/m6-resource-type-inheritance from 7ddf5f5543
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 19s
CI / security (pull_request) Successful in 36s
CI / typecheck (pull_request) Successful in 39s
CI / unit_tests (pull_request) Successful in 2m58s
CI / integration_tests (pull_request) Successful in 3m13s
CI / docker (pull_request) Successful in 39s
CI / coverage (pull_request) Successful in 4m55s
CI / benchmark-regression (pull_request) Successful in 30m47s
to ab4da7804d
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 22s
CI / unit_tests (pull_request) Failing after 26s
CI / security (pull_request) Successful in 35s
CI / typecheck (pull_request) Successful in 37s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Failing after 23s
CI / integration_tests (pull_request) Failing after 2m19s
CI / benchmark-regression (pull_request) Has been cancelled
2026-03-10 03:40:38 +00:00
Compare
hamza.khyari dismissed brent.edwards's review 2026-03-10 03:40:38 +00:00
Reason:

New commits pushed, approval review dismissed automatically according to repository settings

fix(resource): address PR #618 review findings (round 1)
Some checks failed
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / quality (pull_request) Successful in 18s
CI / build (pull_request) Successful in 16s
CI / unit_tests (pull_request) Failing after 25s
CI / security (pull_request) Successful in 34s
CI / typecheck (pull_request) Successful in 36s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Failing after 25s
CI / integration_tests (pull_request) Failing after 2m19s
CI / benchmark-regression (pull_request) Has been cancelled
135481f9a6
- P2-1: Add field_validator('inherits') on ResourceTypeSpec
- P2-2: Remove 'properties' from _COLLECTION_FIELDS (scalar override)
- P2-3: Add behave scenarios for exception rollback paths
- P2-4: Add ordering comment to BUILTIN_TYPES
- P2-5: Define RegistryHost Protocol, remove type:ignore on mixins
- P2-6: Add if_not_exists=True on migration index creation
- P3-1: Use deque.popleft() in get_ancestors for O(1) BFS
- P3-2: Filter private attrs in _to_dict vars() fallback
- P3-3: Replace _handler_cache import with clear_handler_cache()
- P3-4: Add migration downgrade test for m6_004
- P3-5: Extract ResourceDagMixin to _resource_registry_dag.py

All files remain under 500-line CONTRIBUTING limit.
Lint, typecheck, and 85 behave scenarios pass.
fix(migration): chain m6_004 after m7_001_repo_indexing_tables to resolve multiple heads
All checks were successful
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 17s
CI / quality (pull_request) Successful in 17s
CI / security (pull_request) Successful in 34s
CI / typecheck (pull_request) Successful in 37s
CI / unit_tests (pull_request) Successful in 2m35s
CI / integration_tests (pull_request) Successful in 3m7s
CI / docker (pull_request) Successful in 40s
CI / coverage (pull_request) Successful in 4m56s
CI / benchmark-regression (pull_request) Successful in 31m53s
45c15bcc1a
m6_004_resource_type_inherits and m7_001_repo_indexing_tables both had
down_revision='m6_003_async_jobs_table', creating two Alembic heads.
Chain m6_004 after m7_001 so the migration graph is linear.
hamza.khyari scheduled this pull request to auto merge when all checks succeed 2026-03-10 04:11:13 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
cleveragents/cleveragents-core!618
No description provided.