Skip to content

fix: remove orphaned is_inherited from AttributeInterface GraphQL schema (closes #12)#13

Draft
infrahub-bug-pipeline[bot] wants to merge 2 commits intostablefrom
ai-bug-pipeline-12-is-inherited-graphql-d017ddcf7c7956d1
Draft

fix: remove orphaned is_inherited from AttributeInterface GraphQL schema (closes #12)#13
infrahub-bug-pipeline[bot] wants to merge 2 commits intostablefrom
ai-bug-pipeline-12-is-inherited-graphql-d017ddcf7c7956d1

Conversation

@infrahub-bug-pipeline
Copy link
Copy Markdown

@infrahub-bug-pipeline infrahub-bug-pipeline Bot commented May 6, 2026

Why

is_inherited: Boolean was advertised by AttributeInterface in the GraphQL schema, but the corresponding attribute was removed from BaseAttribute in a prior cleanup. Any client that selected this field received an AttributeError: 'String' object has no attribute 'is_inherited' response, with data: null — a hard runtime crash for introspection-driven clients.

Goal: Remove the orphaned schema declaration so the field no longer appears in introspection and the runtime never tries to resolve it.

Non-goals: Re-implementing is_inherited at the resolver or data-model level; updating the Python SDK (python_sdk/infrahub_sdk/node/attribute.py still carries a same-named attribute but that is a separate concern).

Closes #12

What changed

  • Behavioral: is_inherited no longer appears on AttributeInterface or any concrete attribute type in the GraphQL schema. Queries selecting it will now receive a schema validation error rather than a runtime AttributeError.
  • Implementation: Deleted is_inherited = Field(Boolean) from AttributeInterface in backend/infrahub/graphql/types/attribute.py (single line). schema/schema.graphql and frontend/app/src/shared/api/graphql/generated/graphql-env.d.ts were regenerated to reflect the change.
  • What stayed the same: All other attribute fields (is_default, is_protected, updated_at, source, owner) are untouched. No database migration is required. No REST API contract changes.

Fix strategy

The root cause is shallow: a single orphaned field declaration. The is_inherited property was intentionally removed from the runtime model (FlagProperty enum has only IS_PROTECTED; FlagPropertyMixin._flag_properties drives __init__ and never contained is_inherited). The schema declaration should have been removed at the same time.

A targeted one-line deletion is the correct fix. Adding a resolve_is_inherited resolver returning None would keep a dead field in the public schema; adding is_inherited back to FlagProperty/BaseAttribute would re-introduce removed functionality. Neither alternative is correct.

How to review

  • Key file: backend/infrahub/graphql/types/attribute.py — the one-line deletion at AttributeInterface.
  • Generated file (mechanical): schema/schema.graphql — 11 occurrences of is_inherited removed (interface + 10 implementors). Verify with grep is_inherited schema/schema.graphql returning nothing.
  • Generated file (mechanical): frontend/app/src/shared/api/graphql/generated/graphql-env.d.ts — updated by pnpm run codegen:graphql to match the new schema.
  • New test: backend/tests/unit/graphql/types/test_attribute.py — asserts "is_inherited" not in AttributeInterface._meta.fields.

How to test

# Replication test (was failing before the fix)
uv run pytest backend/tests/unit/graphql/types/test_attribute.py::test_attribute_interface_does_not_expose_is_inherited -x -v

# Full unit test suite
uv run invoke backend.test-unit

# Confirm field is gone from generated schema
grep is_inherited schema/schema.graphql  # should return nothing

Expected: 1 test passes, 705 unit tests pass, grep returns no output.

Impact & rollout

  • Backward compatibility: Breaking for clients that explicitly select is_inherited — but those clients were already broken at runtime with AttributeError. Removing the field from the schema converts the hard crash into a clean schema validation error, which is the correct behavior. Any client generating code from introspection that auto-selects all fields will now work correctly.
  • Performance: N/A
  • Config/env changes: None
  • Deployment notes: Safe to deploy; no migration required.

Checklist

  • Tests added/updated
  • Changelog entry added (uv run towncrier create ...)
  • External docs updated (if user-facing or ops-facing change)
  • Internal .md docs updated (internal knowledge and AI code tools knowledge)
  • I have reviewed AI generated content

AGENT_FIX_COMPLETE

Generated by Bug fixer agent for issue #13 · ● 340.9K ·

@infrahub-bug-pipeline
Copy link
Copy Markdown
Author

AGENT_REVIEW_VERDICT: TEST_APPROVED

APPROVED WITH SUGGESTIONS

The test is correct, realistic, and directly validates the stated root cause. Minor non-blocking suggestions below.


A. Test realism

The test imports the real production class (from infrahub.graphql.types.attribute import AttributeInterface) and inspects _meta.fields, which is the authoritative graphene metaclass dictionary that drives schema generation. There are no mock substitutions or fictional string values. _meta.fields is populated at class-definition time, so it is guaranteed to reflect the live schema state. This is the right object to probe.

Confirmed against source: AttributeInterface in backend/infrahub/graphql/types/attribute.py:62-66 declares is_inherited = Field(Boolean) at line 64, which populates _meta.fields["is_inherited"]. FlagProperty (the enum that backs _flag_properties on the runtime model) contains only IS_PROTECTED = "is_protected" — no is_inherited (backend/infrahub/core/constants/schema.py:7-9). The runtime and schema are genuinely out of sync. The test scenario is real.

B. Test correctness

  • The assertion "is_inherited" not in AttributeInterface._meta.fields targets the correct FIXED state. The test fails today because line 64 of attribute.py is still present; it will pass only once that line is removed.
  • The test cannot pass without modifying the production file. There is no alternate code path that would satisfy the assertion on the current codebase.
  • The test exercises the right scope: the orphaned GraphQL schema declaration is the root cause, and removing it from the interface prevents any client from requesting the field — making the to_graphql() crash in backend/infrahub/core/attribute.py:593 unreachable. The two-file root cause collapses to a single fix, and the test correctly covers it.

C. Test quality

  • No fixtures required; _meta.fields is static at import time — fully deterministic.
  • File placement (backend/tests/unit/graphql/types/test_attribute.py) and naming convention match the existing sibling test_upload_scalar.py.
  • Tests observable behavior (the public interface field set), not internal implementation details.

Non-blocking suggestion: Consider adding positive assertions for the fields that should remain (is_default, is_protected, updated_at). This guards against a fix that over-removes fields. Example:

def test_attribute_interface_does_not_expose_is_inherited() -> None:
    fields = AttributeInterface._meta.fields
    assert "is_inherited" not in fields
    # Guard against over-removal of legitimate fields
    for expected in ("is_default", "is_protected", "updated_at"):
        assert expected in fields

This is a suggestion only — the single-assertion form is acceptable for merge.

D. Alignment with analysis

The analyst's root cause: is_inherited = Field(Boolean) at attribute.py:64 is an orphaned schema declaration with no backing runtime attribute. The test directly validates removal of that declaration by checking _meta.fields. Scope is correct — not too narrow, not too broad.

The docstring "the runtime model removed it" is slightly ambiguous (could be read as the graphene BaseAttribute class rather than the Python backend BaseAttribute). Non-blocking, but could be tightened.


Recommended next steps

The fixer agent should remove is_inherited = Field(Boolean) (line 64) from AttributeInterface in backend/infrahub/graphql/types/attribute.py. No changes to backend/infrahub/core/attribute.py are needed — once the field is absent from the schema, no client can request it and to_graphql() will never be called with is_inherited as a field name.

AGENT_REVIEW_ITERATION: test-1

Generated by Bug reviewer agent for issue #13 · ● 137.8K ·

@infrahub-bug-pipeline infrahub-bug-pipeline Bot mentioned this pull request May 6, 2026
@polmichel
Copy link
Copy Markdown
Owner

/bug-fix

@infrahub-bug-pipeline
Copy link
Copy Markdown
Author

infrahub-bug-pipeline Bot commented May 6, 2026

Bug fixer agent completed successfully!

No action needed: empty message received with no task to perform.

@infrahub-bug-pipeline infrahub-bug-pipeline Bot changed the title test: failing test for #12 -- AttributeInterface must not declare is_inherited fix: remove orphaned is_inherited from AttributeInterface GraphQL schema (closes #12) May 6, 2026
…ema (closes #12)

The is_inherited field was removed from BaseAttribute at the runtime level
but its Field(Boolean) declaration on AttributeInterface was left behind,
causing AttributeError on any query that selected it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@infrahub-bug-pipeline
Copy link
Copy Markdown
Author

Commit pushed: 072f9aa

Generated by Bug fixer agent

@infrahub-bug-pipeline
Copy link
Copy Markdown
Author

AGENT_REVIEW_VERDICT: FIX_APPROVED

APPROVED WITH SUGGESTIONS

The fix is correct, minimal, and complete. Two non-blocking suggestions below.


A. Correctness

The fix removes the single orphaned line is_inherited = Field(Boolean) at backend/infrahub/graphql/types/attribute.py:64 (pre-fix line number). This is exactly the root cause identified: a schema declaration with no backing runtime attribute. After the fix:

  • grep is_inherited schema/schema.graphql returns nothing — verified.
  • grep is_inherited frontend/app/src/shared/api/graphql/generated/graphql-env.d.ts returns nothing — verified.
  • No other backend source files reference is_inherited (only the new test and test_types.py excluded list).

The fix is complete for the stated scope. The one-line deletion is correct; adding a resolver or re-implementing the field would both be wrong.

No edge cases are missing given the fix strategy. Any query that previously crashed at runtime with AttributeError will now be rejected cleanly at schema validation. Queries that never selected is_inherited are unaffected.

B. Code quality

Single-line deletion with no unnecessary changes. Changelog entry at changelog/12.fixed.md follows existing format. Generated files (schema/schema.graphql, graphql-env.d.ts) are correctly regenerated.

Non-blocking suggestion — stale exclusion entry: backend/tests/unit/test_types.py:36 lists "is_inherited" in the excluded_fields filter:

excluded_fields = [
    "id",
    "is_default",
    "is_from_profile",
    "is_inherited",   # now dead: field no longer exists on AttributeInterface
    "is_protected",
    ...
]

Since the field no longer exists, this entry never matches and is harmless. But it will mislead future maintainers. Safe to remove in a follow-up.

C. Documentation alignment

Changelog format matches existing entries. PR body documents scope, non-goals, and impact clearly. The Python SDK (python_sdk/infrahub_sdk/node/attribute.py:67,84 and python_sdk/infrahub_sdk/protocols_base.py:58) still carries is_inherited, and the SDK test fixtures (python_sdk/tests/fixtures/unit/test_graphql_plugin/schema.graphql) still declare it — but the PR body explicitly calls this a separate concern and excludes it from scope. That's accurate: updating the SDK is independent work.

D. Test quality

The test at backend/tests/unit/graphql/types/test_attribute.py correctly asserts the fixed state:

assert "is_inherited" not in AttributeInterface._meta.fields

_meta.fields is populated at class-definition time by graphene's metaclass, so the test is deterministic and requires no fixtures. File placement and naming match the sibling test_upload_scalar.py. The test cannot pass on the unfixed codebase and passes on the fixed one.

Non-blocking suggestion (carried from test review): add positive assertions for fields that must remain (is_default, is_protected, updated_at) to guard against over-removal. Still recommended but does not block merge.


Recommended next steps

  1. Merge as-is — the fix and test are correct and complete for the stated scope.
  2. Optional follow-up: remove "is_inherited" from excluded_fields in backend/tests/unit/test_types.py:36.
  3. Separate ticket: update Python SDK (python_sdk/infrahub_sdk/node/attribute.py, python_sdk/infrahub_sdk/protocols_base.py) and its test fixtures to remove is_inherited references.

AGENT_REVIEW_ITERATION: fix-1

Generated by Bug reviewer agent for issue #13 · ● 157.3K ·

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: is_inherited still in GraphQL schema for attribute types, but resolver missing in 1.9.x

1 participant