Skip to content

Stage/v7.6.1: backmerge #1675

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 14, 2025
Merged

Stage/v7.6.1: backmerge #1675

merged 3 commits into from
Jul 14, 2025

Conversation

benwaples
Copy link
Contributor

@benwaples benwaples commented Jul 10, 2025

Description

Back merges the stage/v7.6.1 branch into mainline

Motivation and Context

Resolves BED-6179

Why is this change required? What problem does it solve?

How Has This Been Tested?

Builds successfully, tests pass, and smoke test performed

Screenshots (optional):

Types of changes

  • Chore (a change that does not modify the application functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Database Migrations

Checklist:

Summary by CodeRabbit

  • New Features

    • Introduced feature flag support for "tier management engine" to control the availability of tier management features.
  • Bug Fixes

    • Improved handling of loading and error states when retrieving privilege tag IDs and asset group tags, resulting in more robust UI behavior.
  • Refactor

    • Updated various components and hooks to use a new object-based return format for privilege tag IDs, enhancing consistency and clarity.
    • Centralized feature flag checks to prevent unintended data loading when the feature is disabled.
  • Tests

    • Enhanced and added tests to cover feature flag scenarios and updated test assertions to match new data structures.

@benwaples benwaples self-assigned this Jul 10, 2025
Copy link
Contributor

coderabbitai bot commented Jul 10, 2025

Walkthrough

The changes update the asset group tag ID handling and associated hooks, shifting from returning primitive values to structured objects with status flags. Feature flag checks for "tier_management_engine" are introduced throughout the codebase, affecting hooks, components, and tests. The Go backend function for parsing asset group tag IDs now returns slices, not single integers.

Changes

File(s) Change Summary
cmd/api/src/api/attackpaths.go ParseAssetGroupTagIdWithFallback now returns []int instead of int, accumulates multiple IDs, and adjusts fallback logic accordingly.
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx Hooks now use feature flag gating; return objects containing data and status flags instead of raw values.
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.test.tsx Tests updated for new hook return structure and added feature flag mock handler.
packages/javascript/bh-shared-ui/src/hooks/useZoneParams/useZoneQueryParams.tsx Hook now checks feature flag and returns structured object, only using tag ID when flag is enabled and loaded.
packages/javascript/bh-shared-ui/src/hooks/useZoneParams/useZoneQueryParams.test.tsx Tests updated to mock feature flag and use async assertions for asset group tag ID.
packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneManagement.tsx
.../DetailsRoot.tsx
.../Details/Details.tsx
.../Details/DynamicDetails.tsx
.../InfoHeader.tsx
.../Save/Save.tsx
.../Save/TagForm/TagForm.tsx
.../Summary/Summary.tsx
Components updated to destructure tagId from hook return objects instead of using primitive values.
packages/javascript/bh-shared-ui/src/views/ZoneManagement/DetailsRoot.test.tsx
.../Details/DynamicDetails.test.tsx
.../ZoneManagement.test.tsx
.../Save/TagForm/TagForm.test.tsx
Tests add feature flag mock handler and update async assertions as needed.
packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/Save.test.tsx New test file added for the Save component, with mock server and route parameter setup.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI_Component
    participant useHighestPrivilegeTagId
    participant useFeatureFlag
    participant API

    User->>UI_Component: Interacts with Zone Management UI
    UI_Component->>useHighestPrivilegeTagId: Request highest privilege tag ID
    useHighestPrivilegeTagId->>useFeatureFlag: Check "tier_management_engine" enabled
    useFeatureFlag->>API: GET /api/v2/features
    API-->>useFeatureFlag: Respond with feature flags
    useFeatureFlag-->>useHighestPrivilegeTagId: Return enabled/disabled state
    useHighestPrivilegeTagId-->>UI_Component: Return { tagId, isLoading, isError }
    UI_Component->>UI_Component: Use tagId if available, else handle loading/error
Loading

Possibly related PRs

  • SpecterOps/BloodHound#1603: Modifies the same function (ParseAssetGroupTagIdWithFallback, previously ParseTierIdWithTierZeroFallback) but keeps it returning a single integer, indicating related evolution of this function.

Suggested labels

bug, enhancement, api

Poem

In the warren of code, a new path we find,
With feature flags checked and IDs redefined.
No longer just numbers, but objects we hold,
With loading and errors, their stories are told.
The rabbit hops on, with a hop and a cheer—
“Our asset tags’ future is structured and clear!” 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

mistahj67 and others added 3 commits July 11, 2025 13:06
…back (#1678)

* fix(AGT): add hygiene placeholder id to agt query param fallback

* fix: update zone query params and fix tests

---------

Co-authored-by: Ulises Rangel <[email protected]>
* fix: update tag id usage for object return value

* chore: add unit test checking url based on top tag id
@benwaples benwaples marked this pull request as ready for review July 14, 2025 18:28
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (2)
packages/javascript/bh-shared-ui/src/views/ZoneManagement/Details/Details.tsx (1)

79-80: Good use of optional chaining, but consider handling hook states.

The optional chaining (topTagId?.toString()) correctly handles the potentially undefined tagId. However, the isLoading and isError states returned by the hook aren't being handled, which could lead to suboptimal UX during loading states.

Consider extracting and handling these states:

-const { tagId: topTagId } = useHighestPrivilegeTagId();
+const { tagId: topTagId, isLoading, isError } = useHighestPrivilegeTagId();

This would allow you to show loading indicators or handle error states appropriately.

packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/Summary.tsx (1)

40-41: Consider handling loading and error states from the hook.

The destructuring and optional chaining are implemented correctly. However, the isLoading and isError states from useHighestPrivilegeTagId() aren't being handled, which could result in suboptimal UX during loading states.

Consider extracting these states for better user experience:

-const { tagId: topTagId } = useHighestPrivilegeTagId();
+const { tagId: topTagId, isLoading, isError } = useHighestPrivilegeTagId();
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22cee93 and b3c09f9.

📒 Files selected for processing (18)
  • cmd/api/src/api/attackpaths.go (1 hunks)
  • cmd/ui/src/views/ZoneManagement/InfoHeader.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.test.tsx (3 hunks)
  • packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/hooks/useZoneParams/useZoneQueryParams.test.tsx (3 hunks)
  • packages/javascript/bh-shared-ui/src/hooks/useZoneParams/useZoneQueryParams.tsx (2 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/DetailRoot.test.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/Details/Details.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/Details/DynamicDetails.test.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/Details/DynamicDetails.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/DetailsRoot.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/Save.test.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/Save.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/TagForm/TagForm.test.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/TagForm/TagForm.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/Summary.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneManagement.test.tsx (1 hunks)
  • packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneManagement.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
cmd/api/src/api/attackpaths.go (1)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
Learnt from: mistahj67
PR: SpecterOps/BloodHound#1648
File: cmd/api/src/api/v2/assetgrouptags.go:763-766
Timestamp: 2025-07-09T00:36:54.112Z
Learning: In cmd/api/src/api/v2/assetgrouptags.go, the SearchAssetGroupTags method intentionally fetches all asset group tags and selectors without database-level filtering because it needs to build a complete `kinds` array from all relevant tags for the graph query filter. This allows members to be searched across all tags of the requested type while still filtering the returned tags/selectors by name match.
🧬 Code Graph Analysis (9)
packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/TagForm/TagForm.tsx (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useHighestPrivilegeTagId (64-69)
packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/Save.tsx (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useHighestPrivilegeTagId (64-69)
packages/javascript/bh-shared-ui/src/views/ZoneManagement/DetailsRoot.tsx (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useHighestPrivilegeTagId (64-69)
packages/javascript/bh-shared-ui/src/views/ZoneManagement/Summary/Summary.tsx (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useHighestPrivilegeTagId (64-69)
packages/javascript/bh-shared-ui/src/views/ZoneManagement/Details/DynamicDetails.tsx (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useHighestPrivilegeTagId (64-69)
packages/javascript/bh-shared-ui/src/views/ZoneManagement/Details/Details.tsx (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useHighestPrivilegeTagId (64-69)
packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneManagement.tsx (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useHighestPrivilegeTagId (64-69)
cmd/ui/src/views/ZoneManagement/InfoHeader.tsx (1)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
  • useHighestPrivilegeTagId (64-69)
packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (1)
packages/javascript/bh-shared-ui/src/hooks/useFeatureFlags.tsx (1)
  • useFeatureFlag (52-54)
🔇 Additional comments (29)
packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/TagForm/TagForm.test.tsx (1)

96-107: LGTM: Feature flag mock properly configured

The mock API handler for /api/v2/features is correctly structured and enables the tier_management_engine feature flag for testing. This aligns with the broader refactoring to introduce feature flag gating in the hooks and components.

packages/javascript/bh-shared-ui/src/views/ZoneManagement/ZoneManagement.test.tsx (1)

63-75: LGTM: Consistent feature flag mock implementation

The mock handler follows the same pattern as other test files, ensuring consistent testing behavior across the ZoneManagement components with the tier_management_engine feature flag enabled.

packages/javascript/bh-shared-ui/src/views/ZoneManagement/Details/DynamicDetails.test.tsx (1)

58-70: LGTM: Consistent feature flag mock pattern

The mock handler implementation is identical to other test files, maintaining consistency in how the tier_management_engine feature flag is tested across the ZoneManagement components.

packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/TagForm/TagForm.tsx (1)

111-111: LGTM: Correctly updated hook usage

The change properly adapts to the new useHighestPrivilegeTagId hook return type, which now returns an object with { isLoading, isError, tagId } instead of a primitive value. The destructuring maintains the existing topTagId variable name for consistency.

packages/javascript/bh-shared-ui/src/views/ZoneManagement/Details/DynamicDetails.tsx (1)

57-57: LGTM: Consistent hook usage update

The change correctly updates the useHighestPrivilegeTagId hook usage to destructure the tagId from the returned object, maintaining consistency with the pattern established in other ZoneManagement components.

packages/javascript/bh-shared-ui/src/hooks/useZoneParams/useZoneQueryParams.test.tsx (1)

44-55: Feature flag mock handler looks correct.

The mock API handler for the /api/v2/features endpoint properly simulates the tier_management_engine feature flag being enabled, which aligns with the test requirements.

cmd/ui/src/views/ZoneManagement/InfoHeader.tsx (1)

22-23: LGTM: Correct adaptation to new hook return structure

The destructuring of tagId from useHighestPrivilegeTagId() and the use of optional chaining for topTagId?.toString() properly handles the hook's new return structure where tagId can be undefined during loading or error states.

packages/javascript/bh-shared-ui/src/views/ZoneManagement/DetailsRoot.tsx (1)

22-24: LGTM: Proper handling of updated hook return structure

The destructuring of tagId from useHighestPrivilegeTagId() and the conditional check correctly handle the hook's new return structure where tagId can be undefined during loading or error states.

packages/javascript/bh-shared-ui/src/views/ZoneManagement/DetailRoot.test.tsx (1)

22-41: LGTM: Proper mock server setup for feature flag testing

The mock server setup with the feature flag endpoint is correctly implemented and aligns with the broader changes to support the tier_management_engine feature flag.

packages/javascript/bh-shared-ui/src/views/ZoneManagement/Save/Save.test.tsx (1)

24-58: LGTM: Comprehensive mock server setup

The mock handlers for asset group tags, features, and configuration endpoints are well-structured and appropriate for testing the Save component.

packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.test.tsx (3)

43-54: LGTM: Proper feature flag mock setup

The feature flag mock handler is correctly implemented and aligns with the broader changes to support the tier_management_engine feature flag testing.


82-89: LGTM: Correct test assertion updates

The test assertions have been properly updated to work with the new hook return structure where useOrderedTags returns an object with orderedTags property instead of a direct array.


113-113: LGTM: Correct assertion update for object return

The test assertion correctly accesses result.current.tag.position to work with the new hook return structure.

packages/javascript/bh-shared-ui/src/hooks/useZoneParams/useZoneQueryParams.tsx (6)

20-20: LGTM! Feature flag import added correctly.

The import for useFeatureFlag is properly added to integrate tier management engine feature gating.


36-36: LGTM! Feature flag check implementation is correct.

The feature flag check for tier_management_engine follows the established pattern and will properly gate the tier management functionality.


38-38: LGTM! Updated hook usage aligns with new return structure.

The destructuring of tagId, isLoading, and isError from useHighestPrivilegeTagId correctly handles the updated hook return structure that now includes loading and error states.


40-44: LGTM! Memoized callback prevents unnecessary re-renders.

The setZoneQueryParams callback is properly memoized with useCallback and the correct dependency array, which will prevent unnecessary re-renders when the hook's return value is used in React components.


48-54: LGTM! Early return logic properly handles loading and error states.

The early return when feature flag or tag ID is loading/errored prevents unnecessary processing and ensures consistent behavior. Returning undefined for assetGroupTagId and empty params is the correct fallback behavior.


56-56: LGTM! Correctly uses destructured tagId value.

The parseAssetGroupTagId function now correctly receives the destructured tagId from the updated hook return structure instead of a direct primitive value.

cmd/api/src/api/attackpaths.go (4)

28-29: LGTM! Function signature change to return slice is well-implemented.

The change from returning a single int to []int allows the function to return multiple relevant tag IDs, which is more flexible and aligns with the broader codebase changes for handling multiple asset group tag IDs.


31-43: LGTM! Logic correctly handles specific tag ID cases.

The updated logic properly handles:

  • Parsing errors by returning the slice
  • Hygiene placeholder (ID 0) by appending the constant
  • Valid tag IDs by appending the parsed value
  • Database lookup errors appropriately

All return paths consistently return slices.


45-54: LGTM! Fallback logic returns both hygiene and tier zero IDs.

The fallback behavior correctly returns both the hygiene placeholder ID and the tier zero asset group tag ID when no specific tag ID is provided. This makes sense as it provides comprehensive coverage for default scenarios.


28-54: No callers found for ParseAssetGroupTagIdWithFallback
A search across the repository for usages of ParseAssetGroupTagIdWithFallback returned only its own definition and no call sites. There are currently no callers that need to be updated to handle the new []int return type.

packages/javascript/bh-shared-ui/src/hooks/useAssetGroupTags/useAssetGroupTags.tsx (6)

24-24: LGTM! Feature flag import added correctly.

The import for useFeatureFlag is properly added to enable tier management engine feature gating.


26-39: LGTM! Feature flag gating prevents unnecessary queries.

The feature flag check and query enablement logic correctly:

  • Checks if the feature flag is not loading, not errored, and enabled
  • Only enables the query when the feature flag conditions are met
  • Prevents unnecessary API calls when tier management is disabled

This follows good practices for feature flag implementation.


41-53: LGTM! Updated return structure provides better state management.

The useOrderedTags hook now:

  • Properly destructures loading and error states from useAssetGroupTags
  • Returns an object with orderedTags, isLoading, and isError
  • Maintains the same filtering and sorting logic

This provides consumers with better visibility into the loading and error states.


57-62: LGTM! Consistent return structure for useHighestPrivilegeTag.

The hook correctly:

  • Destructures from the updated useOrderedTags return structure
  • Returns an object with loading/error states alongside the tag data
  • Maintains the same logic for finding the highest privilege tag

This provides consistent state management across all hooks.


64-69: LGTM! Consistent return structure for useHighestPrivilegeTagId.

The hook correctly:

  • Destructures from the updated useOrderedTags return structure
  • Returns an object with loading/error states alongside the tag ID
  • Maintains the same logic for finding the highest privilege tag ID

This aligns with the pattern established by other hooks in the file.


26-69: Verify all consumers of these hooks are updated
The return shapes of useOrderedTags, useHighestPrivilegeTag, and useHighestPrivilegeTagId have changed from primitives to objects. Please search for every usage and update them to destructure the new properties:

Example updates:

- const tags = useOrderedTags();
+ const { orderedTags, isLoading, isError } = useOrderedTags();
- const highestTag = useHighestPrivilegeTag();
+ const { tag: highestTag, isLoading, isError } = useHighestPrivilegeTag();
- const highestTagId = useHighestPrivilegeTagId();
+ const { tagId: highestTagId, isLoading, isError } = useHighestPrivilegeTagId();

Copy link
Contributor

@mistahj67 mistahj67 left a comment

Choose a reason for hiding this comment

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

Code LGTM 🚀

@benwaples benwaples merged commit a240baf into main Jul 14, 2025
9 checks passed
@benwaples benwaples deleted the stage/v7.6.1 branch July 14, 2025 20:00
@github-actions github-actions bot locked and limited conversation to collaborators Jul 14, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants