⚠️ This beta is intended for testing purposes only and should not be used in production environments.
⚠️ This beta release includes a breaking change. Read the Breaking changes section before upgrading — in particular the new cleanup of artifacts whose target has left the artifact definition's target group.
⚠️ Runtime versions. Infrahub 1.11 moves to Python 3.14 (from 3.13) and Neo4j 2026.05.0 (from 2025.10.1). If you run your own Neo4j rather than the version shipped with Infrahub, plan that database upgrade alongside this one.
v1.11.0b0 is the first beta of Infrahub 1.11.0.
The headline of 1.11 is scale and performance. Until now, almost any change caused Infrahub to redo work the change could not possibly have affected: merging a branch re-ran every Generator, regenerated every artifact and could recompute many computed attributes, and a single commit to a linked repository recomputed every transform-based computed attribute in the instance. On a large dataset that meant thousands of background tasks and an instance that stayed busy for tens of minutes after a merge. 1.11 makes that work proportional to what actually changed.
Alongside this, webhook deliveries become first-class tasks you can inspect, retry and cancel. A delivery used to be an anonymous step inside a larger task, so you could not see what was sent, read what came back, or resend anything without re-firing the original event. Each delivery is now its own task with its request, response and a plain-language failure reason.
The release centers on three themes: performance and scale — selective regeneration after a merge, precise Generator and computed-attribute triggers, and narrower schema validation; operability and recovery — webhook delivery operability, and detection of and recovery from a failed merge; and load management — priority lanes for the work users wait on, and API backpressure that protects interactive traffic when the instance is busy.
Main changes
Regenerate only what a merge actually changed
When you merge a branch or a Proposed Change, Infrahub now dispatches only the artifact and Generator definitions whose inputs the merge actually changed, and only for the members affected — instead of regenerating every definition for every member.
A merge captures its own diff and uses it to decide what to run. A definition is dispatched when its GraphQL query, its definition object, or its code changed; members are narrowed to the objects the change touched. Across the scenarios used to validate this, the number of regeneration tasks a merge produces fell by between 73% and 100%.
The rule the selection follows is that regenerating too much is acceptable and regenerating too little is not. Every uncertain signal — a missing or unreadable diff, a definition with no content fingerprint yet, an incomplete dependency list — falls back to regenerating everything for that definition, so nothing is left stale. On a direct merge that runs a Generator, the artifacts that consume that Generator's output are regenerated once the Generator finishes.
The behavior is controlled by selective_execution_after_merge (INFRAHUB_SELECTIVE_EXECUTION_AFTER_MERGE) and is enabled by default. Setting it to false restores the previous full-regeneration behavior.
Re-run only the Generators a commit affected
Within a Proposed Change, a Generator is now re-run only when its source file, its GraphQL query, or its definition was affected by the commits in the branch — not on any file change anywhere in a linked repository. A typo fixed in an unrelated README no longer re-runs every Generator across every member of its target group.
This extends to Generators the precise regeneration that 1.10 introduced for artifacts. Each decision to run or skip is recorded in the Proposed Change's task log, naming the file, query, or definition field responsible, so you can see why a Generator ran. Read-only repositories now participate on the same terms, even on branches where sync_with_git is disabled.
For dependencies Infrahub cannot detect on its own — a helper module imported at runtime from a sibling package, for example — declare them with the optional watch: key on generator_definitions in .infrahub.yml:
generator_definitions:
- name: interface_generator
file_path: "generators/interfaces.py"
class_name: InterfaceGenerator
targets: network-devices
query: device_interfaces
watch:
files:
- "generators/shared/" # directory entries are recursive
- "generators/helpers/naming.py"Generators imported before this upgrade keep working unchanged and adopt the precise behavior automatically on their next import. Until then they conservatively re-run on any file change in their repository, so no output goes stale during the transition.
Recompute computed attributes only when their inputs change
Two separate causes of unnecessary recomputation are addressed.
- A commit to a linked repository used to recompute every transform-based computed attribute in the instance, for every object of its kind. Infrahub now recomputes only the attributes whose Python transform actually changed. An edit and its exact revert leave the transform unchanged and trigger no work at all.
- A schema change used to recompute every computed attribute on the branch. A computed attribute is now recomputed only when the change affects a schema element its value depends on, including elements reached through a relationship. Where the impact cannot be determined, Infrahub still falls back to a full recompute so nothing goes stale.
Underneath both, definitions that produce output from a repository — GraphQL queries, transformations, artifact definitions and Generator definitions — now carry a fingerprint attribute holding a content hash of their inputs. Because the fingerprint is branch-aware, a change to a definition surfaces the same way whether it arrived by a Git import, a branch merge, a rebase or a direct edit.
One behavior to be aware of: a Python transform that declares no watch: key has the commit id folded into its fingerprint, so it continues to recompute on every commit to its repository — but scoped to its own attributes only, rather than to every computed attribute in the instance. Declaring watch: with an explicit list (including an empty one) opts that transform into precise, commit-independent recomputation. The first import of a transform after upgrading recomputes its attributes once; that is a one-time cost, not a per-commit one.
Setting the watch property on any transform is now a best practice to make sure resources are only generated when needed. Here's an example for a Python Transformation that is used to generate a cabling plan artifact:
python_transforms:
- name: cabling_plan
class_name: CablingPlan
file_path: "./transforms/cabling_plan.py"
# cabling_plan.py imports from the src package (protocols), which the transform's
# auto-detected closure (its own directory) cannot follow. Declaring it here closes
# that gap and marks the closure complete, so the artifact only regenerates when a
# real dependency changes instead of on every unrelated commit.
watch:
files:
- src/infrahub_solution_ai_dc/protocols.pyMore information can be found in the documentation.
Return to normal faster after a large merge or rebase
Merging or rebasing a branch now runs a single combined recompute pass for computed attributes, display labels and human-friendly IDs, instead of one recompute job per changed node. The work after the operation scales with the number of derived values actually affected rather than with how many nodes were touched, and the resulting values are unchanged.
Recomputed values are also now written in bulk. Previously each value took its own update task and its own API call; results are now persisted together in bounded transactions. For computed attributes backed by a Python transform, a batch initializes the transform's Git repository once for the whole batch rather than once per object, and an object whose recomputed value is unchanged is skipped entirely — so it neither writes nor triggers further recomputation. That last point removes a self-sustaining cycle in which recomputing already-correct values dispatched more recomputation.
Validate only the constraints a change can violate
Schema constraint validation during Proposed Change checks, merges and rebases used to run against every kind in the schema, even for a branch that changed only data. It is now scoped twice over:
- to the kinds the change touched — plus the generics they inherit from and any kind whose uniqueness constraint reads an attribute of a changed kind;
- to the fields a constraint actually guards — changing an attribute that participates in no uniqueness constraint no longer re-validates that kind's uniqueness, and re-parenting a child no longer triggers the children check on the parent.
The amount of validation work no longer grows with the total size of your schema. Schema diffs between two identical branches now correctly come back empty.
Inspect, retry and cancel webhook deliveries
Every webhook delivery is now its own task, visible in the Tasks tab and from the webhook's related tasks panel. For each delivery you can see the request Infrahub sent, the response it received, and the number of attempts made.
- Failures are readable. An expected delivery failure — an unreachable target, a TLS problem, a timeout, an HTTP error from the receiver, a misconfiguration — is reported as a classified reason with a hint on what to check, instead of a raw Python stack trace. Unexpected errors keep their trace, so a genuine defect still looks like one.
- Deliveries retry on their own. A failed delivery retries automatically with a delay between attempts, rather than exhausting its retries in a few seconds as before. Retries stop early for failures that cannot succeed on a retry, such as a 4xx response or a bad configuration.
- You can retry a settled delivery. Retrying resends the payload that was captured when the event fired, against the webhook's current configuration — so fixing a wrong URL, header or signing key and then retrying works as expected. The original delivery is preserved as a record.
- You can cancel one still in flight. Cancelling a delivery stops its remaining attempts.
A delivery still waiting between automatic attempts is no longer misreported as crashed, and a TLS certificate verification failure against any external endpoint — a webhook target or an SSO provider — is now reported as a TLS error rather than a generic connection error.
Recover from a failed merge
If the worker running a merge is killed partway through, the instance used to be left with a half-merged default branch, a branch stuck in MERGING, and no way back other than restoring a backup or running manual database queries. 1.11 makes that state detectable, visible, and recoverable.
- Writes are blocked for the duration of a merge. Writes to both the default branch and the source branch are refused while a merge runs, and a new merge or rebase is refused while one is in progress. A blocked write receives a transient message asking the caller to retry shortly, and the block lifts automatically when the merge finishes or is rolled back.
- A dead merge is detected. Infrahub notices when a merge's worker is no longer running, records the branch as failed, and keeps the default branch protected. Writes then receive a distinct, non-retryable error telling the operator that recovery is required — separate from the transient "merge in progress" case, so a client does not retry forever.
infrahub recover mergeputs things back. The new command rolls back the partial merge on the default branch, resets the branch and any associated Proposed Change toOPEN, and lifts the write protection. It shows you what it will do and asks for confirmation (--yesto skip), and it is safe to run twice — a run with nothing to recover reports that and changes nothing. Object, attribute and relationship timestamps are restored to their pre-merge values, including for objects affected by a schema migration.
Branch-status rejections now also carry structured GraphQL error codes — BRANCH_ALREADY_MERGED, BRANCH_NEEDS_REBASE and MERGE_IN_PROGRESS — so API and SDK clients can react to a code instead of matching message text.
Keep interactive work moving while background work runs
Background work and the operations a user is waiting on used to share a single queue, so clicking "merge" while the instance worked through a backlog meant waiting behind all of it.
Infrahub now runs three priority lanes and classifies work between them. Branch operations (create, merge, rebase, delete, validate), Proposed Change merges, Generator definition runs, schema load and check, transform rendering and on-demand artifact generation all dispatch on the high lane. Work whose result nobody is waiting on moves to the low lane: profile refresh, and post-merge follow-ups such as Proposed Change cancellation, automatic branch deletion and artifact and Generator regeneration — which previously inherited the merge's own high priority. IPAM reconciliation stays in the middle.
Priority travels through a whole chain of tasks, so a sub-task started by a high-priority operation runs at the same priority. The queues are created automatically on startup, so an upgraded instance needs no manual migration.
Keep the UI responsive when the instance is busy
Heavy background work and the web UI compete for the same finite API and database capacity. Because the API could not tell the two apart, a busy instance could stop serving the UI altogether and the application appeared to hang.
Requests can now declare their priority with an X-Priority header (high, medium or low; anything missing or invalid is treated as medium). Under sustained overload Infrahub sheds lower-priority requests first, returning 429 Too Many Requests with a Retry-After header before the request does any work, so interactive traffic keeps flowing. The web UI sends high on its own calls and low on opt-in background traffic.
Shedding responds to two independent signals: how long requests are waiting for capacity, and how much slower the database is currently responding than its measured best. It is graduated rather than all-or-nothing — as pressure rises, a growing fraction of a priority class is shed, low first, then medium, with high-priority traffic protected until the database is under extreme load. On a normally loaded deployment the layer does nothing. Eight infrahub_admission_* metric families and three database-stress gauges are exposed on /metrics, and the whole layer can be switched off with INFRAHUB_API_BACKPRESSURE_ENABLED=false.
Sort object lists the way you need them
Object lists can now be sorted from the UI, using the backend ordering support added in 1.10. There are two ways in, and they stay in agreement because both read from the page URL.
- From a column header. Clicking a header opens a menu with "Sort ascending", "Sort descending" and "Filter". Columns pointing at a single related object offer a "Sort by" entry listing that object's sortable attributes. The header shows the active direction, and choosing the active direction again returns to the default order. Per-column filtering, previously opened by clicking the header directly, is now the "Filter" item in this menu.
- From the toolbar. A Sort control beside the existing Filter control opens a list of sort entries you can add, reorder by dragging, and remove. Dragging changes which field takes precedence. The control shows a count when a sort is active, and reveals the schema's own default ordering when none is set.
The sort lives in the URL, so a sorted view can be bookmarked and shared. Sorting runs on the server, so large lists are not slower to sort. This works in object lists and in the IPAM IP address and prefix lists.
Set the date and time format you want to read
Dates in the UI followed the browser's locale, and an organisation had no way to set a house format.
Each user can now choose their own date format and timezone from a Preferences card on their account Profile tab, and the choice follows them to any device. Administrators holding the new manage_global_preferences permission set organisation-wide defaults on a Global preferences tab. A field a user leaves unset inherits the organisation default, and falls back to the browser's setting when no default exists. Personal preferences are private: reading or writing them only ever touches your own record.
Delete a repository without emptying it by hand first
Deleting a repository used to fail, because the objects it imports and generates — transforms, checks, GraphQL queries, Generators, and the artifact definitions, artifacts, Generator instances and validators below them — are attached by mandatory relationships that Infrahub correctly refuses to break. The only way through was to delete the whole tree by hand, leaf first.
Deleting a repository now cascades to the objects it owns. Infrahub still refuses the deletion, with a clear error, if a cascaded object is required by something outside the repository, so an incomplete cascade cannot silently leave orphaned data behind. Note that this now also removes validators attached to Proposed Changes and the artifacts and Generator instances they produced, which is the intended ownership relationship.
Merge branches that reordered the same list
Attribute schemas gain an ordered flag. Setting it to false on a List or JSON-array attribute means reordering that attribute's elements is no longer reported as a conflict during a merge or rebase — while adding or removing an element still is.
The built-in enum, dropdown choices, used_by and restricted_namespaces attributes now use this. That resolves a case where two branches ended up holding the same set of dropdown choices in a different order and the branch still could not be rebased.
Minor changes
- Removing a relationship from a schema now closes the underlying relationship data instead of leaving it active but unreachable in the graph. Previously the data was orphaned and stale peers would resurface if a relationship with the same identifier was added again later.
- Markdown artifacts render Mermaid diagrams from
```mermaidcode blocks. - Hierarchical
parentandchildrenrelationships now show the related kind's label — "Region" or "Site", rather than "Parent" and "Children" — in the detail view, tabs, table headers, filters, the sort picker and create and edit forms. - Adding a child to a hierarchical object now pre-fills the parent field with the object you started from.
- The branch "Sync with Git" flag now carries an explanation clarifying that it controls whether an Infrahub-created branch is propagated to Git, and does not indicate whether the branch came from Git.
- The Infrahub container image is significantly smaller: the build toolchain now lives in a build stage excluded from the runtime image, and
numpyandpyarroware no longer installed by default (pyarrowremains available through theobject-transferextra forinfrahubctl object load). - The deprecated
infrahub git-agentcommand has been removed. It was replaced by the task worker some time ago.
Breaking changes
Read this section before upgrading.
Artifacts are deleted when their target leaves the target group
Artifact generation now deletes artifacts whose target object is no longer a member of the artifact definition's target group. Previously those artifacts were left in place indefinitely as stale copies — for example after merging a branch that removed the target from the group.
If any workflow reads those leftover artifacts, capture their content before the target is removed from the group. The deletion happens on the next full generation pass over the definition: after a merge, after a definition update, or on a call to the generate endpoint with no node filter.
A related fix limits this cleanup to passes that examined every member, so a generation run narrowed to specific members no longer deletes artifacts it never looked at.
Full changelog
Added
- Added a
selective_execution_after_mergesetting (envINFRAHUB_SELECTIVE_EXECUTION_AFTER_MERGE) that narrows post-merge regeneration to the artifact and Generator definitions the merge actually affected, instead of regenerating every definition for every member. When enabled, a merge captures its diff and dispatches only the definitions whose query, definition, or code inputs changed, and only for the affected members. Every uncertain signal, such as a disabled flag, a missing or unreadable diff summary, a definition with no computed fingerprint, or an incomplete dependency closure, falls back to full regeneration, so no affected artifact or Generator can be left stale. On a direct merge that runs a Generator, the artifacts consuming that Generator's output are regenerated after the Generator completes. The setting is enabled by default. CoreGraphQLQuery,CoreTransformation,CoreArtifactDefinition, andCoreGeneratorDefinitionnow carry an optional, branch-awarefingerprintattribute. It holds a content hash of the definition's inputs, laying the groundwork for change-driven regeneration. A null value means the node predates the feature or has not been re-imported since; nothing is backfilled.- Webhook deliveries are now first-class, observable tasks. Each delivery runs as its own task, retries automatically on failure, reports a classified failure reason with a remediation hint instead of a raw stack trace for expected delivery errors, records the request it sent and the response it received, and can be retried or cancelled by an operator.
- Writes to the default branch and to the source branch are now blocked for the full duration of a branch merge, and a new merge or rebase is refused while a merge is in progress. Blocked writes receive a transient message asking the caller to retry shortly, and the protection is lifted automatically once the merge completes (or is rolled back).
- Added the
infrahub recover mergeCLI command to recover from a failed branch merge. It rolls back the partial merge on the default branch, resets the branch and any associated proposed change toOPEN, and lifts the write protection so the default branch is writable again. The command is operator-confirmed (skip the prompt with--yes) and idempotent: a run with nothing to recover reports so and makes no changes. While a branch is in the failed state, all mutations against it, including its deletion, are refused until recovery has run. - Branch-status write rejections now carry structured GraphQL error codes in the error
extensions:BRANCH_ALREADY_MERGED,BRANCH_NEEDS_REBASE, andMERGE_IN_PROGRESS.MERGE_IN_PROGRESS(HTTP status 423) signals a transient block while a merge is running and includes the branch being written and the branch being merged, so API and SDK clients can retry on the code instead of matching the message text. - Priority-aware API backpressure. The API now accepts an optional
X-Priorityrequest header (high,medium, orlow; defaults tomediumwhen absent or invalid). Under sustained overload the admission layer sheds lower-priority traffic first, returning429 Too Many Requestswith aRetry-Afterheader while the request handler never runs, so interactivehightraffic keeps flowing. Eight newinfrahub_admission_*Prometheus metric families are exposed on/metricsfor observability. The layer is disabled-inert on a normally-loaded deployment and can be turned off entirely withINFRAHUB_API_BACKPRESSURE_ENABLED=false. - The frontend now declares request priority on every API call via the
X-Priorityheader, sendinghighby default andlowfor opt-in background traffic. The API CORS allowed-headers list now includesx-priorityso the header is accepted from the browser. - Added a database-stress signal derived from the reference permission query and used it to make API load shedding progressively tiered. The signal tracks the query's all-time fastest measured execution time (the "floor", timed over read executions of the query only) and, over a rolling window, how much slower the database currently is; new Prometheus gauges expose it (
infrahub_db_reference_query_floor_seconds,infrahub_db_reference_query_window_min_seconds,infrahub_db_reference_query_stress_ratio_median). Database stress is now an independent shed trigger in the admission layer alongside CoDel: as a class's stress ratio climbs past its trigger, a growing fraction of that class is shed (20% within 1–2x the trigger, 50% within 2–5x, 80% at or beyond 5x) rather than the whole class at once. The triggers are tiered per class (low 5x, medium 20x, high 100x), so low-priority traffic sheds first, then medium, and high-priority (interactive) traffic is protected until the database is under extreme load. Stress-driven sheds are reported under a distinctreason="stress"label oninfrahub_admission_rejected_total. NewINFRAHUB_API_BACKPRESSURE_*settings tune the window, warm-up sample count, per-class stress thresholds, and per-class backstop caps. - Added user and global preferences as internal
StandardNodeobjects (not schema nodes). A singlePreferencemodel holds both layers: one row per user stores that user's overrides, and a separate row stores the organisation-wide defaults (date_format,timezone). Rows are lazily created on first write; a missing row means nothing is set. TheInfrahubEffectivePreferencesGraphQL query returns the merged effective values for the calling user (user override → global default → the client's built-in default), each with the layer it was resolved from. User preferences are private by construction — there is no generic query, and the custom query and mutation only ever touch the caller's own row. - Added a personal Preferences card to the account Profile tab, where each user picks their own date format (from curated presets) and timezone (searchable IANA list). A field left unset inherits the organisation default, or the browser default when none is set.
- Added a Global preferences tab (
/profile/global-preferences) to account settings, visible only to users holding themanage_global_preferencespermission, where administrators set the organisation-wide default date format and timezone that apply to every user without a personal override. - Added the
manage_global_preferencesglobal permission. TheInfrahubSetPreferencesmutation enforces it whenscopeisGLOBAL, and theInfrahubGlobalPreferencesquery enforces it before reading the organisation-wide row (super admins implicitly pass). Writing your own preferences (scope: USER) or reading them (InfrahubUserPreferences) needs no permission — those paths only ever touch the calling account's own row. - Removing a relationship from a schema now closes the relationship data using that schema instead of leaving them active, but unreachable, in the graph. Previously the data was orphaned, which would resurface stale peers if a relationship with the same identifier was re-added later. (#2474)
- Markdown artifacts now render Mermaid diagrams from ```mermaid code blocks.
- Added an
inputstyle variant to the Button component and adopted it for selector triggers (sort, filter, user preferences, number pool) so they match the styling of form inputs.
Changed
-
Breaking: artifact generation now deletes artifacts whose target is no longer a member of the artifact definition's target group. Previously such artifacts were left behind indefinitely as stale copies, for example after merging a branch or proposed change that removed the target from the group. Any workflow that relied on reading those leftover artifacts must capture their content before the target is removed: the next full generation pass over the definition (after a merge, a definition update, or a call to the generate endpoint without a node filter) deletes them. (#9790)
-
When a proposed change includes commits to a linked repository, Infrahub now re-runs only the Generators whose source, GraphQL query, or definition was actually affected by the change, instead of running every Generator in the repository on any file change. This extends the precise regeneration already applied to artifacts. Each run or skip decision is recorded in the proposed change's task log, naming the file, query, or definition field that triggered it. Read-only repositories participate on the same terms, even on branches where
sync_with_gitis disabled. Generators imported before this change keep working unchanged and adopt the precise behavior automatically on their next import; until then they conservatively re-run on any file change in their repository.For dependencies Infrahub cannot detect automatically, such as helper modules imported at runtime from a sibling package, you can declare extra files with the optional
watch:key ongenerator_definitionsentries in.infrahub.yml. Changes to a declared file or directory then re-run that Generator's instances. -
When a Git commit changes a Python transform, Infrahub now recomputes only the computed attributes whose transform actually changed, instead of recomputing every transform-based computed attribute on any commit. The first time a transform is imported it still recomputes its attributes once, so the initial import keeps a one-time recompute cost.
-
Merging or rebasing a branch now runs a single coalesced recompute for computed attributes, display labels, and human-friendly ids instead of one recompute job per changed node. The work after a merge or rebase scales with the number of affected derived values rather than the changed-node count, so a large merge or rebase returns the instance to normal much faster while producing the same derived values.
-
Recomputing a Python-transform computed attribute for a batch of nodes now initializes the transform's git repository once for the whole batch instead of once per node, and persists the results in a single bulk write instead of a GraphQL mutation per node. A node whose recomputed value is unchanged is now skipped, so it neither writes nor triggers a further recompute. This shortens the trailing recompute after a merge or rebase that affects many nodes of the same kind (for example a device-type change that refreshes every device's computed description).
-
Tasks a user is blocked on now run at high priority: branch mutations (create, merge, rebase, delete, validate), proposed-change merge, and generator-definition runs dispatch at high priority when waiting for completion, as do the schema load/check, transform rendering, and on-demand artifact generation endpoints. Profile refresh tasks now run at low priority alongside the other derived-value tasks, and post-merge follow-ups (proposed-change cancellation, automatic branch deletion, artifact and Generator regeneration) run at low priority instead of inheriting high priority from the merge, with IPAM reconciliation kept at medium.
-
Column headers in object lists and IPAM IP address/prefix lists now open a menu to sort the list directly from the column: sortable attribute columns offer "Sort ascending" and "Sort descending", and to-one relationship columns offer a "Sort by" entry listing the related object's sortable attributes. The header shows a direction indicator for the active sort, selecting the active direction again restores the default order, and the sort stays in sync with the toolbar Sort control and the page URL. Per-column filtering, previously opened by clicking the header, is now the "Filter" item in this menu.
-
Hierarchical
parentandchildrenrelationships now display the related kind's label (for example "Region" or "Site") instead of the generic "Parent"/"Children" everywhere they appear — the object detail view, tabs, table column headers, filters, the sort picker, and create/edit forms. -
Added an explanation to the branch "Sync with Git" flag (in both the branch details view and the create-branch form) clarifying that it controls whether an Infrahub-created branch is propagated to Git, and does not indicate whether the branch originated from Git. (#9883)
-
Flow runs that stop sending heartbeats are marked as crashed only after a longer grace period, so a run waiting out an automatic retry is no longer prematurely marked as crashed.
-
Upgraded Python to 3.14 (from 3.13). Upgraded Neo4j to 2026.05.0 (from 2025.10.1).
Fixed
- Branches containing only data changes no longer trigger every validator across every kind in the schema. Schema diffs between identical branches now come back empty, and constraint validation is scoped to the kinds with changed data or schemas. (#2592)
- Schema constraint validation triggered by a data change now runs only the node-level constraints (uniqueness and hierarchy) whose specific field or path was actually modified, instead of every node-level constraint defined on the changed kind. A change to an attribute that participates in no uniqueness constraint no longer re-validates the kind's uniqueness, reducing the work performed during proposed-change checks, merges, and rebase operations.
- Scope computed-attribute recompute to the schema elements a change actually touches. Previously any schema change recomputed every computed attribute on the branch (for Python-transform attributes, one recompute job per object of its kind), regardless of relevance. A computed attribute is now recomputed only when the change affects a schema element its value depends on — including elements reached through relationships — while changes whose impact cannot be determined still fall back to a full recompute so nothing goes stale. (#9415)
- Added an
orderedflag to attribute schemas. When set tofalseon aListorJSON-array attribute, reordering its elements is no longer reported as a conflict during branch merge and rebase (element counts still matter, so adding or removing an element is still a conflict). The built-in schemaenum, dropdownchoices, genericused_by, andrestricted_namespacesattributes now use this so that two branches reordering the same list can be merged without a manual conflict resolution. (#9764) - Artifact generation no longer deletes artifacts that a narrowed run never examined. A run limited to specific members was treated as having evaluated the whole target group, so the stale-artifact cleanup could remove artifacts whose target was still a member. The cleanup now runs only for a pass that examined every member.
- Artifact and Generator regeneration during a proposed change no longer fails when a definition references a repository that has no changes in the branch diff; the missing repository is now skipped instead of aborting the task.
- Selective regeneration no longer skips an artifact or Generator when the change lands on a node the definition's query reads through a relationship. Such a node is not tracked as a member of the query's target group, so narrowing previously resolved it to no subscriber at all and left the output stale; these changes now regenerate every member of the definition instead. This also covers a kind the query reads both directly and through a relationship, which was mistaken for a directly-targeted kind.
- Selective regeneration after a merge now narrows Generator instances to the members actually impacted, instead of rerunning every instance in the group.
- Fixed display labels and human-friendly ids that read across a relationship not refreshing when they were recomputed by their own id. Resolving a cross-node template overwrote the cached own-id filter in place, so a later self recompute queried with the relationship filter, matched no node, and left the stored value stale.
- Rolling back a failed branch merge now fully restores
updated_at/updated_bymetadata. Objects that first landed on the default branch as part of the failed merge have their merge-time stamps cleared instead of keeping the failed merge timestamp, and a recovery that is interrupted partway through can be re-run without leaving merge-time metadata or orphaned graph records behind. - Recovering from a failed branch merge now restores
updated_at/updated_bymetadata for objects, attributes, and relationships affected by a schema migration, such as changing the inheritance of a schema or adding a new attribute. - Webhook task runs are now discoverable from the webhook related tasks panel.
- A TLS certificate verification failure when Infrahub connects to an external HTTPS endpoint (such as a webhook target or an SSO provider) is now reported as a TLS error instead of a generic connection error.
- Fixed some GraphQL requests returning an unexpected HTTP 500 error instead of a proper node-not-found (404) response. (#9926)
- When adding a child to a hierarchical object (for example a Country under a Continent), the creation form now pre-fills the parent field with that object, so the parent no longer has to be selected by hand.
- Fixed the focus ring on text inputs being clipped on the left and right edges when an object form is opened inside a sheet (for example the "Add object" form). The form no longer adds its own redundant scroll container, so the ring is drawn in full.
Removed
- Removed the
infrahub git-agentcommand line utility, which was deprecated long ago and replaced by the task worker. (#5584)
Housekeeping
- Significantly reduced the size of the Infrahub container image: the build toolchain now lives in a dedicated build stage that is excluded from the runtime image, and the
numpyandpyarrowdependencies are no longer installed by default (pyarrowremains available via theobject-transferextra forinfrahubctl object load). - Restructured the frontend tooling around a pnpm workspace: all frontend packages (
app,packages/ui, and theschema-visualizergit submodule) are now members of a single pnpm workspace atfrontend/with one shared lock file; cross-package dependency versions are managed through the pnpmcatalog:; install-time build scripts are disallowed and Playwright versions are pinned viaoverrides; and the node stages ofdevelopment/Dockerfilewere restructured with BuildKit cache mounts for parallel builds and better layer-cache reuse. - Introduced a shared
isRelationshipSchematype guard in the frontend schema entity and routed the previously inline"peer" in fieldSchemafield-schema checks through it, so attribute/relationship discrimination now lives in one tested place instead of being duplicated across form, table, filter and detail components. - Made the Neo4j Bolt connector thread-pool ceiling tunable in the testcontainers stacks via the
INFRAHUB_TESTING_DB_BOLT_THREAD_POOL_MAX_SIZEenvironment variable (defaulting to Neo4j's own400, so normal test runs are unaffected). Heavy dataset/performance runs can raise it to avoidNeo.TransientError.Request.NoThreadsAvailablewhen a wide computed-attribute recompute fan-out saturates the pool.