Skip to content

fix: scope artifact/generator regeneration for complex uniqueness constraints#9487

Open
FragmentedPacket wants to merge 4 commits into
stablefrom
fix/artifact-target-uniqueness-constraints
Open

fix: scope artifact/generator regeneration for complex uniqueness constraints#9487
FragmentedPacket wants to merge 4 commits into
stablefrom
fix/artifact-target-uniqueness-constraints

Conversation

@FragmentedPacket

@FragmentedPacket FragmentedPacket commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

During a proposed change, the artifact/generator validator decides whether it can limit regeneration to the objects that actually changed, or whether it must regenerate every target in the definition's group. That decision is driven by only_has_unique_targets on the GraphQL query analyzer.

Previously a query was recognized as targeting a single object only when it filtered by a required ids argument, or by a node whose schema had exactly one uniqueness constraint made of exactly one attribute (with the filter argument name matching that attribute). Any other shape fell back to regenerating all targets and logged:

Artifact definition <name> query does not guarantee unique targets. All targets will be processed.

This penalized several standard, recommended schema patterns:

  • composite uniqueness constraints (more than one component, e.g. namespace__value + name__value),
  • nodes carrying more than one uniqueness constraint (even when the query fully satisfies one of them),
  • constraints that include a relationship (e.g. uniqueness on name__value + site),
  • filtering by the node's human-friendly id (hfid:).

It also degraded silently: a definition whose query was scoped correctly would start regenerating all targets the moment a second uniqueness constraint was added to the node's schema, with no change to the query or definition.

Change

only_has_unique_targets now applies the same model the uniqueness checker uses. A root query resolves to a single object when:

  • it filters by a required, single-valued ids or hfid argument, or
  • every component of at least one of the node's uniqueness constraints is pinned by a required, single-valued filter argument.

Component pinning:

  • attribute component attr__value → a required, single-valued attr__value argument,
  • relationship component → a required, single-valued <rel>__ids or <rel>__hfid argument, and only when the relationship is single-cardinality.

The fallback path was always functionally correct; this is a scope/performance improvement — more changes now regenerate only the affected artifacts instead of the whole group.

Notes

  • No schema or API changes. The uniqueness-constraint metadata is unchanged; only the analyzer's interpretation of it is broadened.
  • Existing behavior is preserved (including a required ids: $ids list variable counting as unique, as the artifact framework drives it per target).
  • The targets_unique_nodes field description on InfrahubGraphQLQueryReport was updated to match.

Tests

  • Extended test_query_report_single_target coverage with a new test_query_report_single_target_complex_constraints exercising composite, multiple, relationship-based, and hfid constraints (both pinned and unpinned cases).
  • Verified the downstream proposed-change suite (test_validate_artifacts_generation.py) and the InfrahubGraphQLQueryReport resolver tests still pass.

Closes #9486

🤖 Generated with Claude Code


Summary by cubic

Improve artifact/generator regeneration by correctly detecting single-target GraphQL queries using composite, multiple, overlapping, relationship-based uniqueness constraints, or hfid. Also fixes a misclassification when required list variables were used to pin uniqueness components, avoiding unnecessary full-group rebuilds.

  • Bug Fixes
    • Recognize a query as unique when it filters by a required single ids or hfid, or pins every component of at least one uniqueness constraint (including composites, overlapping/multiple groups, and single-cardinality relationships via <rel>__ids/<rel>__hfid).
    • Do not treat a required list-typed variable as a single-valued pin for uniqueness components; only single-element list literals, static values, or required scalar variables count. Still accept a required list variable for top-level ids/hfid.
    • Updated the GraphQLQueryReport.targets_unique_nodes description and regenerated the GraphQL schema. Added tests for composite, overlapping and multi-constraint, relationship, hfid, and list-variable cases.

Written for commit bd452fe. Summary will update on new commits.

Review in cubic

…straints

The proposed-change artifact/generator validator decides whether it can
limit regeneration to the changed objects via `only_has_unique_targets`.
That check previously recognized a query as single-target only when it
filtered by a required `ids` argument or by a node whose schema had exactly
one uniqueness constraint made of exactly one attribute.

As a result, queries that filter by a composite uniqueness constraint, by a
node carrying more than one uniqueness constraint, by a relationship-based
constraint, or by `hfid` were treated as non-unique and forced a full
regeneration of every target in the group on any relevant change.

Generalize the determination to the same model the uniqueness checker uses:
a query targets a single object when it filters by a required single `ids`
or `hfid` value, or when every component of at least one uniqueness
constraint is pinned by a required, single-valued argument. Relationship
components are pinned by a single `<rel>__ids`/`<rel>__hfid` value and only
when the relationship is single-cardinality.

Closes #9486

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@FragmentedPacket FragmentedPacket requested a review from a team as a code owner June 7, 2026 18:28
@github-actions github-actions Bot added the group/backend Issue related to the backend (API Server, Git Agent) label Jun 7, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 4 files

Confidence score: 3/5

  • There is a concrete regression risk in backend/infrahub/graphql/analyzer.py: required list variables are being handled as single-valued filters, which can produce false single-target detection.
  • Given the issue is medium severity (6/10) with high confidence (8/10) and affects query analysis behavior, this introduces meaningful user-facing correctness risk if merged as-is.
  • Pay close attention to backend/infrahub/graphql/analyzer.py - list-variable filter handling may misclassify multi-target queries as single-target.

Shadow auto-approve: would not auto-approve because issues were found.

Re-trigger cubic

Comment thread backend/infrahub/graphql/analyzer.py Outdated
@codspeed-hq

codspeed-hq Bot commented Jun 7, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 12 untouched benchmarks


Comparing fix/artifact-target-uniqueness-constraints (bd452fe) with stable (2a1cff7)1

Open in CodSpeed

Footnotes

  1. No successful run was found on stable (9d65300) during the generation of this report, so 2a1cff7 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

FragmentedPacket and others added 2 commits June 7, 2026 15:43
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…int pin

A required list-typed variable used to pin a uniqueness-constraint component
(e.g. `color__ids: $colors` with `$colors: [ID!]!`) was treated as resolving
to a single value, so a query filtering by such a list could be misclassified
as targeting a single object and skip regenerating some stale artifacts.

Constraint-component pinning now rejects required list variables; only a
single-element list literal, a static value, or a required scalar variable
pins a component. The top-level `ids`/`hfid` target selector keeps accepting a
required list variable, since the artifact framework drives it per target
member.

Issue identified by cubic.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@FragmentedPacket

Copy link
Copy Markdown
Contributor Author

Addressed in b71208b — thanks, this was a valid catch identified by cubic.

The flagged line treated any required variable (including a list-typed one) as single-valued. For the top-level ids/hfid target selector that is intentional and preserved — the artifact framework drives that filter per target member, so a required list resolves to one object (this is the existing QUERY_UNIQUE_TARGETS contract).

The real risk was in the new uniqueness-constraint pinning path: a relationship component filtered by a required list variable (e.g. color__ids: $colors with $colors: [ID!]!) could carry several ids and match several objects, yet was being counted as a single target. That direction causes under-regeneration, so it's the dangerous one.

Fix: constraint-component pinning now rejects required list variables (only a single-element list literal, a static value, or a required scalar variable pins a component); the top-level selector keeps the per-member leniency via an explicit allow_required_list flag. Added regression coverage:

  • color__ids: $colors with $colors: [ID!]! → not unique
  • TestingTShirtComposite(ids: $ids) with $ids: [ID!]! → unique (documents the intentional top-level divergence)

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

0 issues found across 2 files (changes from recent commits).

Shadow auto-approve: would require human review. This change modifies core artifact/generator regeneration logic by broadening how unique targets are detected, which directly impacts production behavior for proposed changes and could introduce subtle bugs in scoping artifact regeneration for composite, multiple, relationship-based, or hfid...

Re-trigger cubic

…etection

Add a node with `[["name__value"], ["name__value", "serial_number__value"]]`
to verify that satisfying the smaller subset group alone marks the query as
single-target, while pinning only the field unique to the larger group does
not.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

0 issues found across 1 file (changes from recent commits).

Shadow auto-approve: would require human review. This change modifies core logic for determining artifact regeneration scope, which could lead to under-generation if detection is incorrect, and despite thorough testing and a safe fallback, the complexity justifies a human review.

Re-trigger cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

group/backend Issue related to the backend (API Server, Git Agent)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Artifact/generator regeneration is not scoped to changed targets for composite, multiple, relationship-based, or HFID uniqueness constraints

1 participant