Skip to content

fix: prevent phantom team diffs caused by slug vs name mismatch#997

Draft
fgenois-coveo wants to merge 5 commits into
github:main-enterprisefrom
fgenois-coveo:fix/DT-8803/teams-phantom-diff-slug-matching
Draft

fix: prevent phantom team diffs caused by slug vs name mismatch#997
fgenois-coveo wants to merge 5 commits into
github:main-enterprisefrom
fgenois-coveo:fix/DT-8803/teams-phantom-diff-slug-matching

Conversation

@fgenois-coveo

@fgenois-coveo fgenois-coveo commented Jun 15, 2026

Copy link
Copy Markdown

Problem

When running safe-settings in NOP mode (or any diff-reporting path), teams are incorrectly reported as additions + deletions even when nothing has changed.

Root cause: MergeDeep.processArrays() resolves item identity via NAME_FIELDS, but slug was missing from that list. For teams, the GitHub API returns both name (display name like "Dev Tooling") and slug (like "dev-tooling"). YAML configs use the slug value in the name field. Since the API object's name differs from the YAML's name, MergeDeep treats them as different items.

The sync path in teams.js already handles this correctly (existing.slug === attrs.name.toLowerCase()), so the mismatch only affects diff reporting.

Fix

  1. Add slug (first position) to NAME_FIELDS — API team objects now resolve identity via slug, while YAML objects (which lack slug) resolve via name. Both produce the same value → correct match.

  2. Add title to NAME_FIELDS — milestones use title as identity but it was missing from the list.

  3. Remove the hardcoded if (item.slug) return item.slug special case — the priority-ordered NAME_FIELDS handles this uniformly.

  4. Guard compareDeepIfVisited against mixed primitive/object arrays — prevents crashes when branch protection compares users: ["test"] against users: [{login: "test", type: "User"}].

  5. Strip identity fields per-item — when source uses name and target uses slug, each side strips its own identity field before deep comparison.

Backward compatible — no other plugin returns objects with a slug field, and title is only used by milestones (which have no name field).

Test matrix

Source (YAML) Target (API response) Expected Before After
{name: "dev-tooling", permission: "admin"} {name: "Dev Tooling", slug: "dev-tooling", permission: "admin"} No diff ❌ Add+Delete ✅ No diff
{name: "dev-tooling", permission: "push"} {name: "Dev Tooling", slug: "dev-tooling", permission: "admin"} Modification ❌ Add+Delete ✅ Modification
{name: "My Team", permission: "admin"} {name: "My Team", slug: "my-team", permission: "admin"} No diff ✅ No diff ✅ No diff
{name: "new-team", permission: "push"} (empty) Addition
(empty) {name: "Old Team", slug: "old-team"} Deletion

Teams in YAML use slug format (e.g. name: "my-team") while the GitHub
API returns display name (e.g. name: "My Team") with a separate slug
field. This caused phantom diffs in NOP mode: teams reported as
additions + deletions when no actual changes exist.

The fix adds slug-aware matching in processArrays addition/deletion
filters. When a target item has a slug field, it's compared against
the source name (which is the slug in safe-settings convention).

This is backward compatible: items without slug (labels, rulesets)
continue to match by name as before.
@fgenois-coveo fgenois-coveo force-pushed the fix/DT-8803/teams-phantom-diff-slug-matching branch from 43a788a to 39b0bdb Compare June 15, 2026 20:26
@fgenois-coveo fgenois-coveo changed the title fix: slug-aware matching in MergeDeep to prevent phantom team diffs in NOP mode fix: prevent phantom team diffs caused by slug vs name mismatch Jun 16, 2026
Add 'slug' (first) and 'title' (last) to NAME_FIELDS for teams and
milestones identity matching. Remove hardcoded slug special case in
favor of unified lookup. Guard compareDeepIfVisited against mixed
primitive/object arrays.
@fgenois-coveo fgenois-coveo force-pushed the fix/DT-8803/teams-phantom-diff-slug-matching branch from 39b0bdb to 6e205ac Compare June 16, 2026 17:07
Comment thread lib/mergeDeep.js Outdated
Comment thread lib/mergeDeep.js
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant