fix(resource-registry): implement get_parents() on ResourceDagMixin #3252
No reviewers
Labels
No labels
auto/needs-reevaluation
controller-managed
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Bounty
$100
Bounty
$1000
Bounty
$10000
Bounty
$20
Bounty
$2000
Bounty
$250
Bounty
$50
Bounty
$500
Bounty
$5000
Bounty
$750
MoSCoW
Could have
MoSCoW
Must have
MoSCoW
Should have
Needs Feedback
Points
1
Points
13
Points
2
Points
21
Points
3
Points
34
Points
5
Points
55
Points
8
Points
88
Priority
Backlog
Priority
CI Blocker
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
State
Completed
State
Duplicate
State
In Progress
State
In Review
State
Paused
State
Unverified
State
Verified
State
Wont Do
Type
Automation
Type
Bug
Type
Discussion
Type
Documentation
Type
Epic
Type
Feature
Type
Legendary
Type
Refactor
Type
Support
Type
Task
Type
Testing
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
cleveragents/cleveragents-core!3252
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/v3.7.0-resource-dag-mixin-get-parents"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Adds the missing
get_parents()method toResourceDagMixinin_resource_registry_dag.py, resolving the failing BDD scenario "Get parents returns all direct parents" inresource_dag.feature.Problem
ResourceDagMixin(and thereforeResourceRegistryService) hadget_children()but was missing the symmetricget_parents()method required by the specification. The BDD scenario@dag_traversal"Get parents returns all direct parents" could not pass because the method did not exist.Solution
Added
get_parents(name_or_id: str) -> list[Resource]toResourceDagMixininsrc/cleveragents/application/services/_resource_registry_dag.py. The implementation mirrorsget_children()exactly but queriesResourceLinkModelbychild_idinstead ofparent_id:show_resource()first → raisesNotFoundErrorfor non-existent resourcesResourceLinkModel.filter_by(child_id=resource.resource_id)to find all parent linkslist[Resource](by name, then resource_id)ResourceRegistryServicethrough the existing mixin inheritance chainTest Results
resource_dag.featurepass (including the new "Get parents returns all direct parents" scenario)nox -s typecheck— 0 errors, 0 warningsnox -s lint— all checks passedFiles Changed
src/cleveragents/application/services/_resource_registry_dag.py— addedget_parents()method (35 lines)Closes #2844
Automated by CleverAgents Bot
Supervisor: Implementation | Agent: ca-issue-worker
ResourceDagMixinmissingget_parents()method —resource_dag.featurescenario "Get parents returns all direct parents" has no implementation #2844🔒 Review claimed by reviewer-pool-1 [claim-token: reviewer-pool-1-3252-1775374200]
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-continuous-pr-reviewer
🔒 Review claimed by reviewer-pool-1 [claim-token: reviewer-pool-1-3252-1775373000]
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-continuous-pr-reviewer
✅ Independent Code Review — Approved
Summary
This PR adds the missing
get_parents()method toResourceDagMixin, completing the symmetric DAG traversal API alongside the existingget_children(). The implementation is correct, well-structured, and aligns with the specification.Note: Self-approval is blocked by Forgejo. This review documents the independent code review findings. The PR is approved from a code quality perspective.
Review Checklist
Specification Alignment ✅
get_parents(name_or_id: str) -> list[Resource]matches the spec-required signature from issue #2844get_children()exactly, queryingResourceLinkModelbychild_idinstead ofparent_idNotFoundErrorfor non-existent resources viashow_resource()— fail-fast pattern ✅ResourceRegistryServicethrough the mixin inheritance chain ✅Code Quality ✅
Raisessection (actually more complete thanget_children()'s docstring)# type: ignoresuppressions in new code ✅Correctness ✅
filter_by(child_id=resource.resource_id)finds all parent linksstr(link.parent_id)cast matches the pattern inget_children()finallyblock — no resource leaksshow_resource()call validates input before opening session — fail-fast ✅Test Quality ✅
step_dag_get_parentsandstep_dag_n_parentsare properly implemented@dag_traversaland DAG scenarios continue to pass (no regression)Security ✅
show_resource()filter_by()prevents SQL injectionPR Metadata ✅
ISSUES CLOSED: #2844footerCloses #2844in PR bodyType/Buglabel presentMinor Observation (Non-blocking)
The N+1 query pattern (one query per link to fetch the parent
ResourceModel) is inherited fromget_children(). This is a pre-existing pattern and not introduced by this PR. A future optimization could use a singleINquery, but that's out of scope here.Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer
ResourceDagMixinmissingget_parents()method —resource_dag.featurescenario "Get parents returns all direct parents" has no implementation #2844Independent Code Review — PR #3252
Focus Areas: architecture-alignment, module-boundaries, interface-contracts
Summary
This PR adds the missing
get_parents()method toResourceDagMixin, completing the symmetric DAG traversal API alongside the existingget_children(). The change is a single 35-line method addition to_resource_registry_dag.py, resolving issue #2844.I reviewed the full file on the branch (293 lines), the master baseline (262 lines), the
RegistryHostprotocol in_resource_registry_ops.py, the commit message, PR metadata, and the linked issue's acceptance criteria.Architecture Alignment ✅
get_parents()is added toResourceDagMixinin_resource_registry_dag.py, which is the designated module for DAG traversal operations. This respects the architectural split between instance operations (_resource_registry_ops.py) and DAG operations (_resource_registry_dag.py).Resource) viadb_resource_to_domain(), not raw DB models. Correct abstraction level.show_resource(name_or_id)is called before opening a session, raisingNotFoundErrorimmediately for invalid inputs. This follows the project's fail-fast argument validation pattern.get_children()exactly — same session management, same sorting convention, same guard for missing rows. This is the correct approach for a symmetric operation.Module Boundaries ✅
ResourceLinkModel,ResourceModel,db_resource_to_domain,Resource)._-prefixed, correctly indicating it's internal. External code accessesget_parents()throughResourceRegistryServicevia the mixin inheritance chain.Interface Contracts ✅
get_parents(name_or_id: str) -> list[Resource]satisfies the contract defined in issue #2844.RegistryHostprotocol: The method usesself.show_resource()andself._session(), both already declared in theRegistryHostprotocol. No protocol update needed.NotFoundErrorfor non-existent resources (viashow_resource()). Documented in the docstring'sRaisessection.list[Resource]with deterministic ordering (by name, then resource_id). Returns empty list for root nodes with no parents.ResourceRegistryServiceinherits fromResourceDagMixin, soget_parents()is automatically exposed without additional wiring.Code Correctness ✅
filter_by(child_id=resource.resource_id)correctly finds all parent links for the given resource. This is the symmetric inverse ofget_children()'sfilter_by(parent_id=...).str(link.parent_id)matches the pattern inget_children()(str(link.child_id)).finally: session.close()ensures no resource leaks.# type: ignorein new code ✅Commit & PR Metadata ✅
fix(resource-registry): implement get_parents() on ResourceDagMixin— Conventional Changelog format ✅ISSUES CLOSED: #2844✅Closes #2844✅Type/Bug✅Test Quality ✅
nox -s typecheckandnox -s lintpass cleanly.Observations (Non-blocking)
Pre-existing
# type: ignore[override]onlink_child(line ~42) andunlink_child(line ~113): These violate CONTRIBUTING.md's "no type: ignore" rule but are not introduced by this PR. The newget_parents()method correctly avoids this pattern. Consider filing a separate issue to address the pre-existing suppressions.N+1 query pattern: The method queries each parent
ResourceModelindividually in a loop, rather than using a singleINquery. This is inherited from theget_children()pattern and is not introduced by this PR. For DAGs with many parents this could be a performance concern, but it's out of scope for this bug fix.Silent skip on orphaned links: The
if parent_row is not Noneguard silently skips links where the parent resource no longer exists (data integrity issue). This matchesget_children()'s behavior and is a pre-existing design decision. In a strict fail-fast world, a warning log or error might be appropriate, but changing this pattern is out of scope.Docstring improvement:
get_parents()includes aRaisessection thatget_children()lacks. This is actually an improvement — consider backfilling theRaisessection onget_children()in a future cleanup.Decision
No blocking issues found. The implementation is correct, well-structured, follows existing patterns, and satisfies all acceptance criteria from issue #2844. The code aligns with the specification's DAG traversal requirements and respects all module boundaries and interface contracts.
Automated by CleverAgents Bot
Reviewer: Code Quality | Agent: ca-pr-self-reviewer
Independent Code Review — PR #3252 (APPROVED)
Focus Areas: architecture-alignment, module-boundaries, interface-contracts
Summary
This PR adds the missing
get_parents()method toResourceDagMixinin_resource_registry_dag.py, completing the symmetric DAG traversal API alongside the existingget_children(). The change is a single ~31-line method addition resolving issue #2844.I independently reviewed the full file on the branch (293 lines, 11486 bytes), the master baseline (262 lines, 10274 bytes), the
RegistryHostprotocol in_resource_registry_ops.py, theResourceRegistryServicemixin inheritance chain, the BDD feature fileresource_dag.feature, the commit message, and all PR metadata.Architecture Alignment ✅
get_parents()is added toResourceDagMixinin_resource_registry_dag.py— the designated module for DAG traversal operations. This respects the architectural split between instance operations (_resource_registry_ops.py), DAG operations (_resource_registry_dag.py), and the public service facade (resource_registry_service.py).Resource) viadb_resource_to_domain(), not raw DB models. Correct abstraction level for the application service layer.show_resource(name_or_id)is called before opening a session, raisingNotFoundErrorimmediately for invalid inputs. This follows the project's fail-fast argument validation pattern per CONTRIBUTING.md.get_children()exactly — same session management pattern, same deterministic sorting convention, same guard for missing rows. This is the correct approach for a symmetric DAG operation.Module Boundaries ✅
ResourceLinkModel,ResourceModel,db_resource_to_domain,Resource)._-prefixed, correctly indicating it's internal. External code accessesget_parents()throughResourceRegistryServicevia the mixin inheritance chain.ResourceRegistryService(ResourceInstanceMixin, ResourceDagMixin)inherits fromResourceDagMixin, soget_parents()is automatically exposed without additional wiring. Confirmed by readingresource_registry_service.py.Interface Contracts ✅
get_parents(name_or_id: str) -> list[Resource]satisfies the contract defined in issue #2844's acceptance criteria.RegistryHostprotocol compliance: The method usesself.show_resource()andself._session(), both already declared in theRegistryHostprotocol. No protocol update needed.NotFoundErrorfor non-existent resources (viashow_resource()). Documented in the docstring'sRaisessection.list[Resource]with deterministic ordering (by name, then resource_id). Returns empty list for root nodes with no parents.Code Correctness ✅
filter_by(child_id=resource.resource_id)correctly finds all parent links for the given resource. This is the symmetric inverse ofget_children()'sfilter_by(parent_id=...).str(link.parent_id)matches the pattern inget_children()(str(link.child_id)).finally: session.close()ensures no resource leaks.# type: ignorein new code ✅Commit & PR Metadata ✅
fix(resource-registry): implement get_parents() on ResourceDagMixin— Conventional Changelog format ✅ISSUES CLOSED: #2844✅Closes #2844✅fix/v3.7.0-resource-dag-mixin-get-parentsmatches issue metadata ✅Type/Bug✅Test Quality ✅
a1e50453) on both master and branch.nox -s typecheckandnox -s lintpass cleanly.Observations (Non-blocking)
Pre-existing
# type: ignore[override]onlink_child(~line 42) andunlink_child(~line 113): These violate CONTRIBUTING.md's "no type: ignore" rule but are not introduced by this PR. The newget_parents()method correctly avoids this pattern. Consider filing a separate issue to address the pre-existing suppressions.N+1 query pattern: The method queries each parent
ResourceModelindividually in a loop, rather than using a singleINquery. This is inherited from theget_children()pattern and is not introduced by this PR. For DAGs with many parents this could be a performance concern, but it's out of scope for this bug fix.Silent skip on orphaned links: The
if parent_row is not Noneguard silently skips links where the parent resource no longer exists (data integrity issue). This matchesget_children()'s behavior and is a pre-existing design decision, not introduced here.Docstring improvement:
get_parents()includes aRaisessection thatget_children()lacks. This is actually an improvement over the existing pattern — consider backfilling theRaisessection onget_children()in a future cleanup.Decision
No blocking issues found. The implementation is correct, well-structured, follows existing patterns precisely, and satisfies all acceptance criteria from issue #2844. The code aligns with the specification's DAG traversal requirements and respects all module boundaries and interface contracts.
Decision: APPROVED ✅
Automated by CleverAgents Bot
Supervisor: PR Review | Agent: ca-pr-self-reviewer