Merge release-1.11 into develop - #10236
Closed
infrahub-github-bot-app[bot] wants to merge 123 commits into
Closed
Conversation
GroupMutatedEvent.get_related() built an unbounded related-resources list (2 entries per member, 3 per ancestor), so a single mutation changing a few hundred members exceeded the Prefect maximum and the whole event was silently dropped: no activity log, no membership automations. Members and ancestors are now a single entry each (dropping the duplicate related.node and the dead group.update roles) and the list is truncated at the Prefect maximum, so the event is always recorded. The event query filter and related-nodes output read all three related-node roles and dedupe by id, keeping output identical across the consolidated format and older events still in Prefect retention. Fixes #10127
The resource loop in sort_key wrote the resource priority to type_cost instead of item_cost. Every item carries both a type marker and a resource marker, so that loop always ran and always erased the type priority the first loop had computed. Items were therefore sorted by resource kind only, and an integration test could run before the smoke test of the same resource. Nothing covered the ordering, so add unit tests on the collection hook.
Bumps [dorny/paths-filter](https://github.com/dorny/paths-filter) from 4.0.2 to 4.0.3. - [Release notes](https://github.com/dorny/paths-filter/releases) - [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md) - [Commits](dorny/paths-filter@v4.0.2...v4.0.3) --- updated-dependencies: - dependency-name: dorny/paths-filter dependency-version: 4.0.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
…le/dorny/paths-filter-4.0.3 chore(deps): bump dorny/paths-filter from 4.0.2 to 4.0.3
The "Example:" preview and the (i) source tooltip in the preferences forms formatted through an ad-hoc date-fns format() call that took no timezone, so they showed browser-local time — off by the zone offset, and at some hours by a whole calendar day (with ISO_8601 they also stated a false offset). The duplicate formatter is deleted; previews now render through the shared preference-aware mechanism, exposed as formatWithPreferences, using the unsaved timezone the form currently holds. The user form falls back to the effective inherited zone, while the global editor falls back to the browser zone, since an unset global timezone means "browser default" for every viewer. Fixes #10175 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ative DateDisplay's compact/relative heuristic compared the day difference with a signed value, so any future date — however distant — fell through to the relative branch and rendered as "in 4 years" instead of the user's preferred date format. The window is now a week either side of now, keeping the relative phrase where it reads best and the preferred date beyond. Fixes #10173 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The object detail page's DateTime attribute renderer entered DateDisplay's metadata-oriented age heuristic, so user-authored timestamps collapsed to a bare date (or a relative phrase) while the list view showed the full preferred datetime. The detail renderer now opts out with fullTimestamp, matching the table cell. Fixes #10172 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bumps [CodSpeedHQ/action](https://github.com/codspeedhq/action) from 4 to 5. - [Release notes](https://github.com/codspeedhq/action/releases) - [Changelog](https://github.com/CodSpeedHQ/action/blob/main/CHANGELOG.md) - [Commits](CodSpeedHQ/action@v4...v5) --- updated-dependencies: - dependency-name: CodSpeedHQ/action dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Bumps [actions/stale](https://github.com/actions/stale) from 10 to 11. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](actions/stale@v10...v11) --- updated-dependencies: - dependency-name: actions/stale dependency-version: '11' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
… cleared The date-format example fell back to the caller's effective timezone while the form's timezone field was empty. When that effective value came from the caller's own override, clearing the override previewed the zone being removed rather than the one about to apply. The fallback is now the inherited zone, expressed as a domain rule: a USER source resolves to null, since the API resolves the inherited value away once an override wins and the browser zone is the only honest stand-in. Also trims the comments added across these fixes down to the load-bearing rationale, per the repo's code-documentation style. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s only when exposed (#10195)
…preview inheritedTimezone excluded a USER source but let any other source through, so a DEFAULT source carrying a value would have been reported as inherited — contradicting the pattern resolver, which discards a DEFAULT value even when one is present. Only GLOBAL is an inherited layer, so whitelist it. No behaviour change against today's API, which always pairs DEFAULT with a null value; this keeps the two rules consistent if that ever loosens. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A worker without a local copy of a repository clones it on demand. That path only carries the repository id and name, so the repository object never learns which git ref to track. It fell back to the platform default branch. Git then failed on any repository whose default branch is not `main`, the repository went to error, and artifact generation failed with it. It takes more than one worker to see this. Each worker keeps its own git directory, so only the worker that added the repository has the clone. `InfrahubRepositoryBase` gets one abstract method, `resolve_checkout_ref`. The re-clone path calls it and passes the result to `create_locally`. `CoreRepository` reads `default_branch`, `CoreReadOnlyRepository` reads `ref`. The lookup only runs when the clone is missing, so an existing clone costs nothing extra. Fixes #8749
…ferences-issue-ce071b
Both branch-details suites assert that no "default" text is visible on a non-default branch, to prove the default badge is absent. The assertion used a substring match, so renaming the attribute label to "Schema differs from default branch" made it match that label instead and the tests failed for a page that is rendering correctly. The positive assertion was updated with the rename; this negative one was missed. Matching exactly still catches a stray badge, whose text is only "default", without tripping on prose that contains the word. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(backend): delete branch data in bounded batches Deleting a large branch ran as a single Cypher statement whose peak transaction memory scaled with the size of the branch: it collected the element id of every vertex touched by a deleted edge into two lists and concatenated them. On a big enough branch that exceeded dbms.memory.transaction.total.max, and because the inner writes committed in batches the failure left the branch stranded in DELETING -- invisible in the branch list -- with most of its data still in the graph. Replace it with a set of bounded queries driven from Python by a new BranchDeleter component: the agnostic peers of branch-only nodes first, then one batch of edges per relationship type until none are left. Peak transaction memory is now a function of the batch size rather than the branch, and naming the relationship type lets the branch range index serve the match instead of scanning every edge in the database. Each batch also deletes the vertices its edge deletions left bare. That is only sound because every branch edge is removed by the batch's DELETE and both endpoints are re-examined afterwards, so the batch that removes a vertex's last edge is the one that sees it at degree zero. A DETACH DELETE would break it by removing edges that never reach a batch of their own, stranding the vertices on their far side. Branch.delete now raises instead of silently dropping only the Branch vertex; callers use BranchDeleter. On a branch of 1,074,217 edges this deletes the same 416,021 vertices as before, in 27.8s rather than 87.9s, and completes at a 512 MiB transaction limit where the previous implementation ran out of memory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(backend): finish deleting branches abandoned in DELETING A branch delete that ran out of transaction memory committed part of its work before failing, leaving the branch with the DELETING status and the rest of its data in the graph. That state was unreachable: the branch is filtered out of the branch list, and Branch.get_by_name hides it by default, so the delete could not be retried and the space could not be reclaimed. Add graph migration 075, which finds every branch still in DELETING and runs it through BranchDeleter, then removes the branch node. It reuses the normal delete path rather than reimplementing it, loading each branch via the ignore_deleting escape hatch. Finding them needs a dedicated query because the shared branch list query filters DELETING out unconditionally. BranchDeleter now returns the number of edges it removed so the migration can report progress on the migration console. `infrahub db migrate` raises the infrahub log level to WARNING, which hides the deleter's own logging, and deleting a large branch takes long enough that silence looks like a hung upgrade. The migration is a no-op where no branch is in DELETING, and safe to re-run: an interrupted delete resumes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(backend): allow retrying a branch delete left in DELETING A delete that failed part way through set the DELETING status before doing any cleanup, and both the BranchDelete mutation and the branch-delete flow looked the branch up with the default lookup, which hides that status. The retry reported the branch as missing, so the only way to reclaim the data was the upgrade migration -- which runs once, leaving any later failure stranded for good. Both lookups now pass ignore_deleting=False. Two consequences worth knowing: the mutation will accept a delete for a branch whose delete is still running, and nothing serialises the two runs (each query involved is idempotent); and a retry re-emits BranchDeletedEvent. Making the surrounding flow steps idempotent is left for later. Cap the agnostic cleanup at 500 rows rather than query_size_limit. Those batches count Nodes, and each one can drag an unbounded number of peer vertices into the transaction with it, unlike the edge batches where one row is one edge. 500 is what this phase used before the batching work, and raising it to 5000 was incidental rather than deliberate. Drop the edge accounting comment claiming the vertex cleanup can remove extra edges. That was true while the cleanup used DETACH DELETE; it now only deletes vertices that are already bare. Add the missing tests: Branch.delete refusing, the default/global guards that moved onto BranchDeleter, and retrying a branch left in DELETING. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * test(backend): own the branch in the delete-retry test The test created the branch with `create_branch`, which leaves created_by as the system user, so the BranchDelete mutation took its permission branch and the assertion depended on how a super-admin grant resolves against a specific DELETE_BRANCH check. That resolved differently in CI and the test failed with PermissionDeniedError. Use the existing first_account / session_first_account fixtures and make that account the branch owner, so created_by matches the requesting account, the permission check is skipped, and the result turns on the DELETING status alone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(backend): make the DELETING branch cleanup migration robust Address review findings on migration 075 and the deleter's accounting. The migration's branch lookup suppressed the generated pagination. A read query with no limit of its own is executed page by page, so with the SKIP/LIMIT suppressed every page re-read the whole set and the paging never reached a short page: with at least query_size_limit stranded branches the upgrade would never finish. Pagination is enabled again and the lookup orders by branch name, which is unique, so the pages are disjoint. Each branch is now deleted in its own try. A failure is reported as "branch '<name>': <error>" and the loop continues, so an operator gets the names of everything that still needs a re-run instead of the first exception and an unknown remainder. Failing to list the branches at all still aborts, since there is then nothing to iterate. Cap the agnostic cleanup at min(batch_size, 500) rather than a fixed 500, so lowering the configured batch size to fit a constrained database is not answered with a larger batch than was asked for. Count the agnostic cleanup's edges towards the total the deleter reports. It detaches peer vertices, so leaving it out made the migration's per-branch progress undercount for any branch with agnostic data. The 075 test asserted the stalled branch's node was unreachable from main, which it always was -- it only ever existed on that branch, so the assertion held whether or not the migration ran. It now reads the node on its own branch, before and after. A third case covers one branch of three failing, using a FailingBranchDeleter that delegates to the real deleter for the others so the test proves they were reclaimed rather than merely attempted. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(backend): only run post-delete work when this attempt removed the branch BranchDeleter.delete returned the number of edges it removed, which only the upgrade migration used, for its progress output. It said nothing about whether this attempt was the one that removed the branch -- so now that a delete can be retried, two attempts on the same branch could both go on to cancel the proposed changes, emit BranchDeletedEvent and delete the Git branch. Return a BranchDeleteResult carrying both branch_deleted and edges_removed. Removing the vertex is itself the claim: two attempts are serialised on it, so exactly one reports nodes_deleted, with no window of the kind a read followed by a delete would leave. The branch-delete flow returns early when it did not make the claim, and the migration reports whether the branch was still there. Skip the DELETING status write when the status is already set. It is a wasted query for a branch whose earlier delete failed part way through, and it fails outright if the branch has meanwhile been removed -- which made delete() unsafe to call twice at all, whatever it returned. A narrower window remains: an attempt that has not yet written the status, and whose write lands after another attempt removed the vertex, still raises rather than reporting false. It fails the run instead of double-processing, and closing it properly wants a branch-scoped lock. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * make sure branch is deleted from git during concurrent deletes * move delete_branch flow logic to a new component for easier testing * formatting * add default/global branch delete guard at higher level --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
update Branch.get_by_name to include ignore_deleting=True so that a delete can be retried
pass the "low_context" into the branch delete orchestrator for follow-up tasks
Keep stable's behavior from #10195 on top of the 1.11 frontend architecture: git-repository.tsx links to the node's concrete __typename using the moved NodeCore/object-urls modules, and the profiles-field test is ported onto getObjectForEditingFromApi, which replaced generateObjectEditFormQuery. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…C-3001] The git agent relied on the SDK client default for sync_with_git when creating an imported branch in the graph. That default flipped from True to False in the SDK, so branches discovered on a repository remote were created without the flag. Merging such a branch, running its repository checks and generating its artifacts are all gated on sync_with_git, so the git side of a merge was silently skipped: the git branch was never merged into the repository default branch and the recorded commit never advanced. Closes #10208
…d-run A dependency discovered after Vite's initial scan triggers a re-optimization reload that resets vi.mock, which surfaces as "mockClear is not a function" in whichever spec happens to be running. CI hit it on two different files across consecutive runs. @radix-ui/react-dropdown-menu is a declared app dependency the scan misses, so pre-bundling it removes that trigger. Two entries already in this list, @dagrejs/dagre and html-to-image, still reload: they belong to a workspace package consumed as source and do not resolve from the app, so pre-bundling silently skips them. Fixing that needs them declared here, which is a dependency change of its own. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(core): cap and consolidate group mutation event related resources
Merge stable into release-1.11
…10254) * fix: close branch-owned edges when renaming an attribute on a branch The rename ended the old attribute by creating a deleted edge beside it. That is the only option for an edge owned by the default branch, which a branch cannot modify, but it was applied to every edge — so a node created on the branch kept the old attribute active and deleted at the same time. Rebasing then re-stamped both edges to the rebase time, leaving the resolution of "the latest edge for this attribute" a tie between them. The rename dispatched again after the rebase could therefore resolve the still-active edge, rename a second time, and leave the node with two copies of the renamed attribute. Edges the branch owns are now closed with a `to` time, the way the default branch already handled them, and only edges owned by another branch are shadowed. The per-edge-type query rendering is gone with it: direction now comes from the stored edge rather than from the schema's declared direction, so the ten generated UNION branches collapse into four subqueries keyed on direction, and the node match uses a label alternation instead of a three-way UNION. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * rewrite changelog * fix: keep the rename node-selection overridable and cover the rerun Inlining the node match into query_init made Migration012RenameTypeAttributeData's render_match() override dead code, dropping the guard that skips nodes already holding the new attribute name. The match moves back behind render_match() so a subclass can still narrow it. The regression test now re-executes the migration and asserts nothing further is created, matching its sibling tests. That is the pass the rebase makes in practice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add a "Format schema files" section to the Create and load schema page: default key-ordering behaviour, --diff / --check, and the opt-in --strip-defaults / --sort-by-order-weight / --backfill-order-weight flags. DO NOT MERGE until an infrahub-sdk release that ships `infrahubctl schema format` is published (feature merged in opsmill/infrahub-sdk-python#1189). Merging earlier would document a command absent from the released CLI. Co-authored-by: Alex Gittings <agitting96@gmail.com>
docs: rework IPAM documentation
`infrahub tasks flush stale-runs` only reset flow runs stuck in RUNNING, so runs picked up by a worker that died before the flow started stayed PENDING forever. Prefect's start_time filter falls back to expected_start_time, so never-started runs are aged correctly by the existing cutoff. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Playwright's `name` option substring-matches by default, and the branch selector renders the current branch name on every page. A base36 random suffix can therefore spell a word the suites locate by, making that locator match two elements and killing the test with a strict-mode violation. That is what took down object-relationships.spec.ts on #10287: the branch was object-relationshipsaveyj8q5g6r, where the prefix's trailing "s" and a suffix starting "ave" spell "save", so getByRole("button", { name: "Save" }) matched both the Save button and the branch selector. It presents as an unrelated test failing for no reason and passing on re-run, which is why it kept being written off as noise. Switch the TypeScript generator to hex, which the pytest helper already used. Measured over 2M generations against the names the suites locate by: base36 produces 168 collisions (about 1 in 12k), hex produces 0. It is not only "save" -- the sample also threw up branchesmainzvsrjh8, ...u6vl6xpathb and ...t6nname02n, colliding with "main", "path" and "name". None of the 436 distinct locator names across both suites is spellable in hex, so this closes the class rather than the one instance. Both helpers now carry the constraint so a wider alphabet does not creep back in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> (cherry picked from commit 4cf2b30)
The fix rests on the branch suffix staying hexadecimal, but that was
guarded only by the comments added alongside it. Prose already failed once
here: these two helpers silently diverged despite the Python one
documenting itself as a port of the TypeScript one, which is how base36
survived long enough to spell "save" in a branch name.
Assert the suffix matches [0-9a-f]{12} on both sides. Verified by
regression, not just by passing: reverting the TypeScript helper to
Math.random().toString(36) fails both of its cases, and perturbing the
Python helper's output fails its.
The Python test needs a shard marker because CI selects with
-m shard_<name> and an unmarked test is deselected in every shard. It also
inherits the suite's compose stack, since pytest-base-url's autouse
_verify_url pulls in base_url; that costs nothing in CI where the stack is
already running, and there is nowhere else the helper is importable from.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit ecb56d0)
Bump the prefect-redis pin from 0.2.12 to the latest release, 0.2.14. Infrahub only consumes it through the Prefect server env vars (PREFECT_MESSAGING_BROKER/CACHE, PREFECT_SERVER_EVENTS_CAUSAL_ORDERING, PREFECT_SERVER_CONCURRENCY_LEASE_STORAGE), so the change is confined to the Prefect server side. - pyproject.toml: prefect-redis==0.2.12 -> ==0.2.14 - uv.lock: regenerated with uv 0.11.8 (CI's version); prefect 3.7.5 and redis 6.0.0 unchanged, no other package moved except a pendulum marker refresh that was already stale in the lock Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…igration (#10298) NodeRelationshipRemoveMigration overrides SchemaMigration.execute to open its own transaction rather than delegating to the base, which discarded the retry_db_transaction decorator applied to the base method. A transient error inside that transaction failed the migration instead of being replayed. Document the rule where migrations are authored, since an override silently dropping an inherited decorator is easy to repeat. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The stable merge collided on `infrahub tasks flush stale-runs`: release-1.11 moved the purge behind `FlowRunRetention`, while stable widened the state filter to include PENDING via `STALE_FLOW_RUN_STATES`. Keep both — stable's call site alone would not import, since release-1.11 dropped `PrefectTask`. `FlowRunRetention.purge` filters on the same `start_time before cutoff`, so never-started runs still age off Prefect's `expected_start_time` fallback as the original fix intended. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adjacency conflict in `requires-dist`: stable bumped prefect-redis to 0.2.14 and release-1.11's prometheus-client pin sits on the following line. Keep both entries; `uv lock --check` passes against pyproject.toml. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…table test: generate hex branch names so they cannot spell locator words (backport #10291)
Stable to release 1.11
* fix(backend): omit task related nodes with unresolvable kind A Prefect flow run can be tagged with a related node ID that no longer resolves to an active node (deleted definition, stale tag). The kind then stays None and the non-nullable TaskRelatedNode.kind GraphQL field crashed the resolver, breaking the Tasks page. Filter such entries out of the related-nodes accessors so the task is returned without the unrenderable node instead of erroring. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(backend): assert resolvable related node survives kind filtering Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ease-1-11 # Conflicts: # backend/infrahub/task_manager/event/models.py
`stable` fixed `RelatedNodesInfo.get_related_nodes` in `task_manager/models.py` (#9664), filtering out related nodes whose kind never resolved so the non-nullable `TaskRelatedNode.kind` GraphQL field stops crashing the Tasks page. `release-1.11` split that flat module into `task_manager/event/` and `task_manager/flow_run/`. Git's rename detection followed the split to `event/models.py` and reported the conflict there, but `RelatedNodesInfo` actually landed in `flow_run/models.py` — every consumer (`flow_run/enrichment.py`, `reader.py`, `service.py`) imports it from `.models` there, and `event/models.py` does not reference it at all. Drop the incoming block from `event/models.py` (restoring it byte-for-byte to the `release-1.11` version) and apply the two-hunk fix to `flow_run/models.py`, where the target methods were still identical to the merge-base so the patch applies with exact context.
`release-1.11` added `TestRelatedNodesInfo` in #9810; `stable` added the kind filter in #9664. Neither side touched the other's file, so the merge was clean and five of those tests then failed. They assert registration mechanics (`add_node`, `add_nodes`, cross-flow sharing, dedup) but observe through `get_related_nodes`, which now omits nodes whose kind never resolved. `add_node` leaves `kind=None`, so each saw an empty list. Set a kind on the registered nodes, following the idiom already used by `test_get_related_nodes_as_dict` in this file. The assertions are otherwise untouched and no longer depend on whether the accessor filters. Add two tests for the filter's contract, which previously had only component-level coverage: - `test_get_related_nodes_omits_unresolvable_kind` pins the fix across all three accessors. Verified it fails when the filter is reverted. - `test_get_unique_related_node_ids_keeps_unresolved_kind` guards the other direction: `flow_run/enrichment.py` resolves kinds from that list, so filtering it would leave every kind permanently unresolved.
Merge stable into release-1.11
The default test stack carried no com.github.* labels at all, and the cluster stack only had them on database-core2/core3, so containers left behind on the runners could not be traced back to the run that spawned them. com.github.job falls back to GITHUB_JOB because only the three E2E workflow jobs export JOB_NAME, while the integration, functional, docker-integration, benchmark and version-upgrade jobs — the ones that actually build testcontainers stacks — do not. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Merge stable into release-1.11
…10250) * refactor: consolidate graph-integrity checks behind verify_graph() Six overlapping graph-integrity checks were split across infrahub.database.validation and tests.helpers.db_validation, each called individually, so a suite had to know all six existed and new suites routinely covered only a subset. They now live in one module behind a single entry point: verify_graph() raises GraphValidationError listing every violation found rather than stopping at the first failing check, and collect_graph_violations() returns them for tests that assert a damaged state. The individual checks are private. An optional kinds filter narrows every check at its MATCH anchor, so a suite can scope a run to the kinds it touches; checks anchored on Attribute or Relationship vertices resolve the label through the Node the vertex hangs off. The duplicate-attributes check no longer takes a branch. It derives the branch set from the graph, so one call covers every branch that touched a node instead of only the one the caller passed, and it prunes to nodes actually holding two same-named Attribute vertices so a healthy graph does almost no work. It also groups per vertex rather than per uuid, since a kind update leaves several vertices sharing one uuid. The three checks that resolve default-branch visibility now use branched_from rather than created_at as the fork point, matching the production read path. A rebased branch does see default-branch edges written after its creation, so created_at under-included them and the checks could miss a violation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: correct the graph checks surfaced by the consolidated verify_graph() Running every check at every call site exposed gaps in the checks themselves. An empty kinds list rendered an empty label expression, so `MATCH (n:)` made every check fail rather than run unfiltered. It now means no filter, as callers expect. The orphaned-edge and duplicate-attribute checks selected a default-branch edge as visible from a branch, then required it to be open, so an edge closed after the branch forked counted as inactive even though the branch still reads it. Both now allow the same `to > branched_from` exception the relationship-edge-count check already applied. The edges-after-delete check compared a branch's edges against the default branch's delete time. A rebase re-stamps every open edge to the fork point, including the ones recording the branch's own deletion, so those edges looked like writes to an already deleted node. An edge on another branch is now only flagged when it was written strictly after the fork point. The orphaned-edge check also carried a deletion timestamp through four projections without ever reading it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor: rewrite the orphaned-edge check around the delete that caused it The check resolved, per field and peer, the latest second-level edge visible from the branch that deleted the field, then asked whether it was active. Reading a default branch edge through a branch's fork point made a value the default branch had since replaced look live, so an ordinary update after a branch deleted a field was reported as an orphan. It now starts from the delete itself. Every branch holding a deleted or closed first-level edge for a node and field is a candidate; the edges that branch holds decide whether it deleted the field at all, and the last of them to close is when the field went away. A delete writes the field's edges at that same moment, so what is left open, or carries a later time, is what did not follow the delete. Grouping on the node uuid rather than the vertex replaces the guard for kind updates: those copy the node vertex, and both copies point at the same field, so an edge left open on either of them means the field is still in use. Edges on a branch that forked after the delete are checked too, since that branch inherits it. A rebase re-stamps the edges it carries to the fork point, so only what comes after that was written with the delete in view. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: scope an inherited delete to the branches that can see it A delete only reaches a branch that was cut after it from the branch it happened on. Branches are cut from the default branch, so a delete on one branch is invisible to every other, but the check treated any branch forked later as having inherited it and reported that branch's own writes as orphaned. Two sibling branches, one deleting a field and the other adding to it, were enough to trigger it. The violation also named the branch that did the deleting rather than the branch holding the edge, which pointed at the wrong place to go looking. It now names where the edge lives, and where the delete came from when they differ. The duplicate-attribute check now counts a default-branch edge that closed after a branch forked, which the branch still reads as open. Unlike the orphaned-edge check this resolves one edge per field vertex, so a value the default branch replaced cannot be counted twice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix migration --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
add v1.11.0 release notes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merging release-1.11 into develop after merging pull request #10235.
Summary by cubic
Brings release-1.11 to develop. Improves branch deletion safety and retryability; hardens schema updates/rebases and migration retries; fixes Git default-branch/remote-import behavior; consolidates and budgets events; stabilizes recompute scoping and tagging; rejects empty artifact payloads and tightens JSON Schema; advances number pools past taken values; validates timezones and improves date rendering; dedupes large diff trees; and preserves earliest creator during merges.
Branch.delete()now raises; delete via bounded, retryableBranchDataDeleterandBranchDeleteOrchestrator. Follow‑ups (cancel proposed changes, emit event, delete Git branch) run only when this attempt removed the branch. Default/global branches are refused. Graph migration m075 resumes DELETING branches and finishes cleanup; the GraphQL mutation and flow allow retrying a DELETING branch.default_branchand raise on rejection; branches discovered on a remote are created withsync_with_gitenabled.watch.filesfor helpers; exported JSON Schema forbids unknown fields and declares read‑only fields as deprecated.Review and rollout
Branch.delete()calls withBranchDataDeleteror the branch‑delete task/mutation.default_branchonCoreRepository; read‑only repositories continue to useref.watch.files; auto‑detection now includes only the entry file.Written for commit 3972f9a. Summary will update on new commits.