Skip to content

Preserve Agent Type when merging agents without auto-populate#7828

Open
acwhite211 wants to merge 5 commits into
mainfrom
issue-5072
Open

Preserve Agent Type when merging agents without auto-populate#7828
acwhite211 wants to merge 5 commits into
mainfrom
issue-5072

Conversation

@acwhite211
Copy link
Copy Markdown
Member

@acwhite211 acwhite211 commented Mar 19, 2026

Fixes #5072

Found a simple fix to the issue. When record merging is opened with auto-populate disabled, the merged record no longer starts from schema defaults. It now preserves unanimous values from the selected records, so merging two Person agents no longer silently changes AgentType to Organization while still leaving real conflicts available for review.

Checklist

  • Self-review the PR after opening it to make sure the changes look good and
    self-explanatory (or properly documented)
  • Add relevant issue to release milestone
  • Add pr to documentation list

Testing instructions

Confirm it working on any test database, as well as confirming it's working on the kuaccessions_2024_07_05 database.

  • In user preferences, disable Record Merging > Auto-populate.
  • Build an Agent query that returns only records with AgentType = Person.
  • Select two Person agents and open Merge Records.
  • Verify that with Show conflicting fields only enabled, Agent Type is not shown as a conflict.
  • Disable Show conflicting fields only.
  • Verify that Agent Type in the merged record is still Person, not Organization.
  • Complete the merge.
  • Open the merged agent record.
  • Verify that Agent Type is still Person.

Summary by CodeRabbit

  • Refactor
    • Merge dialog now builds the initial merged record based on the "auto-populate" preference and the chosen target record, improving consistency when starting a merge.
    • When auto-populate is turned off, certain fields (e.g., agent type for Agent records) are prefilled from the selected target to reduce manual entry.

Review Change Stack

@github-project-automation github-project-automation Bot moved this to 📋Back Log in General Tester Board Mar 19, 2026
@acwhite211 acwhite211 added this to the 7.12.1 milestone Mar 19, 2026
@acwhite211 acwhite211 requested review from a team March 19, 2026 16:44
@CarolineDenis CarolineDenis requested a review from alesan99 March 19, 2026 17:33
Copy link
Copy Markdown
Collaborator

@emenslin emenslin left a comment

Choose a reason for hiding this comment

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

  • Verify that Agent Type in the merged record is still Person, not Organization.
  • Verify that Agent Type is still Person.

Agent type is still set to Organization, also even with the auto-populate preference disabled, the fields are still auto-populating.

03-23_09.29.mp4

@github-project-automation github-project-automation Bot moved this from 📋Back Log to Dev Attention Needed in General Tester Board Mar 23, 2026
@grantfitzsimmons grantfitzsimmons modified the milestones: 7.12.1, 7.12.2 Apr 14, 2026
Copy link
Copy Markdown
Collaborator

@bhumikaguptaa bhumikaguptaa left a comment

Choose a reason for hiding this comment

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

  • Verify that Agent Type in the merged record is still Person, not Organization.
  • Verify that Agent Type is still Person.

--

Agent type is still set to Organization even with auto-populate being disabled.

Image

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c23e373a-03a6-422e-b5ca-ab042224c331

📥 Commits

Reviewing files that changed from the base of the PR and between 66b1266 and b85b593.

📒 Files selected for processing (2)
  • specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts
  • specifyweb/frontend/js_src/lib/components/Merging/index.tsx

📝 Walkthrough

Walkthrough

Extracts initial-merged-resource construction into an exported async helper buildInitialMergedResource; the merging dialog imports and uses this helper instead of performing conditional merge/post-processing or calling addMissingFields inline.

Changes

Initial merged resource builder extraction

Layer / File(s) Summary
Initial merged resource helper
specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts
Adds exported async buildInitialMergedResource that conditionally runs autoMerge(..., cautious=false, targetId) then postMergeResource when shouldAutoPopulate is true, or returns addMissingFields(table.name, getNonAutoPopulatedFields(...)) when false; also adds getNonAutoPopulatedFields and getTargetResource.
Import cleanup
specifyweb/frontend/js_src/lib/components/Merging/index.tsx
Removes direct addMissingFields import and updates ./autoMerge import to include buildInitialMergedResource.
Dialog payload construction refactor
specifyweb/frontend/js_src/lib/components/Merging/index.tsx
Replaces inline conditional that built mergedPayload with a call to buildInitialMergedResource(table, initialRecords.current, shouldAutoPopulate, target.id).
🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Automatic Tests ⚠️ Warning PR adds exported function buildInitialMergedResource for the bug fix but lacks automatic tests for this function and its getNonAutoPopulatedFields helper that preserves Agent Type. Add tests for buildInitialMergedResource covering both shouldAutoPopulate paths and Agent-specific agentType preservation logic.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding logic to preserve Agent Type when merging agents without auto-populate enabled.
Linked Issues check ✅ Passed The changes implement the core requirement from #5072 by preserving unanimous Agent Type values through buildInitialMergedResource, preventing silent defaults to Organization.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the Agent Type preservation issue; no unrelated modifications were introduced.
Testing Instructions ✅ Passed Testing instructions correctly specify disabling Auto-populate in preferences, selecting Person agents, and verifying Agent Type is preserved, matching the implementation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-5072

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (2)
specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts (1)

57-69: 💤 Low value

Nice, focused extraction — logic preserves unanimous values when auto-populate is off. 👍

One small thing: the as SerializedResource<AnySchema> cast on line 68 covers both branches, but only the postMergeResource branch actually needs it (its declared return type is IR<Awaited<ReturnType<typeof mergeField>>>). You can keep the cast scoped to that branch and let the autoMerge branch stay strongly typed.

♻️ Optional: narrow the cast to the branch that needs it
 export async function buildInitialMergedResource(
   table: SpecifyTable,
   resources: RA<SerializedResource<AnySchema>>,
   shouldAutoPopulate: boolean,
   targetId?: number
 ): Promise<SerializedResource<AnySchema>> {
-  return (shouldAutoPopulate
-    ? await postMergeResource(
-        resources,
-        autoMerge(table, resources, false, targetId)
-      )
-    : autoMerge(table, resources, true, targetId)) as SerializedResource<AnySchema>;
+  if (!shouldAutoPopulate) return autoMerge(table, resources, true, targetId);
+  const merged = await postMergeResource(
+    resources,
+    autoMerge(table, resources, false, targetId)
+  );
+  return merged as SerializedResource<AnySchema>;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts` around lines
57 - 69, The returned value from buildInitialMergedResource is being
blanket-cast to SerializedResource<AnySchema>, but only the postMergeResource
branch actually needs that cast; narrow the cast to the postMergeResource branch
by removing the trailing as SerializedResource<AnySchema> and instead apply the
cast directly to the await postMergeResource(...) call (keeping autoMerge(...)
uncast so its inferred type remains strong). Update inside
buildInitialMergedResource, referencing the postMergeResource(...) and
autoMerge(...) branches so only the postMergeResource result is cast.
specifyweb/frontend/js_src/lib/components/Merging/index.tsx (1)

253-274: ⚡ Quick win

Consider reusing buildInitialMergedResource here too for symmetry. 🙂

The "Auto Populate" button still inlines postMergeResource(records, autoMerge(table, records, false, target.id)), which is exactly the shouldAutoPopulate === true branch of the new helper. Routing this through buildInitialMergedResource keeps both call sites in lockstep, so any future change (e.g., another preservation tweak like the AgentType fix) only needs to land in one place.

Note the subtle pre-existing difference: this button uses the current records rather than initialRecords.current — please confirm that's still the intent before swapping in the helper.

♻️ Proposed refactor
           <Button.Success
             onClick={(): void =>
               loading(
-                postMergeResource(
-                  records,
-                  autoMerge(table, records, false, target.id)
-                )
+                buildInitialMergedResource(table, records, true, target.id)
                   .then(async (merged) => {
                     // REFACTOR: move all this to postMergeResource?
                     const mergedResource = deserializeResource(
                       merged as SerializedResource<AnySchema>
                     );
                     if (merged !== undefined)
                       await runAllFieldChecks(mergedResource);
                     return mergedResource;
                   })
                   .then(setMerged)
               )
             }
           >

If this lands, autoMerge and postMergeResource can likely be dropped from the ./autoMerge import list in this file as well.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/frontend/js_src/lib/components/Merging/index.tsx` around lines 253
- 274, Replace the inline postMergeResource(autoMerge(...)) call inside the
Button.Success onClick with a call to the helper
buildInitialMergedResource(table, records, true, target.id) (or whatever
signature it uses for the shouldAutoPopulate === true path), then pipe its
returned resource through the same deserializeResource, runAllFieldChecks (if
merged !== undefined) and setMerged logic currently present; make sure to
reference and preserve deserializeResource, runAllFieldChecks, and setMerged,
and confirm you intentionally want to use the current records variable (not
initialRecords.current) before swapping in the helper so behavior remains the
same.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts`:
- Around line 57-69: The returned value from buildInitialMergedResource is being
blanket-cast to SerializedResource<AnySchema>, but only the postMergeResource
branch actually needs that cast; narrow the cast to the postMergeResource branch
by removing the trailing as SerializedResource<AnySchema> and instead apply the
cast directly to the await postMergeResource(...) call (keeping autoMerge(...)
uncast so its inferred type remains strong). Update inside
buildInitialMergedResource, referencing the postMergeResource(...) and
autoMerge(...) branches so only the postMergeResource result is cast.

In `@specifyweb/frontend/js_src/lib/components/Merging/index.tsx`:
- Around line 253-274: Replace the inline postMergeResource(autoMerge(...)) call
inside the Button.Success onClick with a call to the helper
buildInitialMergedResource(table, records, true, target.id) (or whatever
signature it uses for the shouldAutoPopulate === true path), then pipe its
returned resource through the same deserializeResource, runAllFieldChecks (if
merged !== undefined) and setMerged logic currently present; make sure to
reference and preserve deserializeResource, runAllFieldChecks, and setMerged,
and confirm you intentionally want to use the current records variable (not
initialRecords.current) before swapping in the helper so behavior remains the
same.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b91c343b-a434-40fc-b6b1-5bd0068ccbb9

📥 Commits

Reviewing files that changed from the base of the PR and between 66b1266 and 8a98db8.

📒 Files selected for processing (2)
  • specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts
  • specifyweb/frontend/js_src/lib/components/Merging/index.tsx

Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts (1)

76-83: 💤 Low value

Consider a registry pattern for table-specific preserved fields.

The current implementation hardcodes 'Agent'/agentType inline. It works and is appropriately scoped to the reported bug, but as soon as a second table needs similar treatment (e.g., other type-discriminated tables), this conditional will grow into a chain. A small lookup map keyed by table name — mirroring the postProcessors pattern already used below in this file — would scale better and keep buildInitialMergedResource tidy.

♻️ Sketch
-const getNonAutoPopulatedFields = (
-  table: SpecifyTable,
-  resources: RA<SerializedResource<AnySchema>>,
-  targetId?: number
-): Partial<SerializedResource<AnySchema>> =>
-  table.name === 'Agent'
-    ? { agentType: getTargetResource(resources, targetId)?.agentType }
-    : {};
+const nonAutoPopulatedFieldExtractors: Partial<
+  RR<
+    keyof Tables,
+    (
+      resources: RA<SerializedResource<AnySchema>>,
+      targetId?: number
+    ) => Partial<SerializedResource<AnySchema>>
+  >
+> = {
+  Agent: (resources, targetId) => ({
+    agentType: getTargetResource(resources, targetId)?.agentType,
+  }),
+};
+
+const getNonAutoPopulatedFields = (
+  table: SpecifyTable,
+  resources: RA<SerializedResource<AnySchema>>,
+  targetId?: number
+): Partial<SerializedResource<AnySchema>> =>
+  nonAutoPopulatedFieldExtractors[table.name]?.(resources, targetId) ?? {};

Also, the helper name reads as "fields that were not auto-populated"; something like getPreservedFieldsWithoutAutoPopulate may communicate intent more directly. Both points are purely stylistic — feel free to defer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts` around lines
76 - 83, The function getNonAutoPopulatedFields currently hardcodes the
Agent/table->agentType mapping; replace this with a small registry lookup (map
keyed by table.name) similar to the existing postProcessors pattern and have
getNonAutoPopulatedFields consult that registry to return preserved fields
(e.g., for 'Agent' return { agentType }) so additions require only registry
updates; also consider renaming the helper to
getPreservedFieldsWithoutAutoPopulate and update callers such as
buildInitialMergedResource to use the registry-backed helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts`:
- Around line 76-83: The function getNonAutoPopulatedFields currently hardcodes
the Agent/table->agentType mapping; replace this with a small registry lookup
(map keyed by table.name) similar to the existing postProcessors pattern and
have getNonAutoPopulatedFields consult that registry to return preserved fields
(e.g., for 'Agent' return { agentType }) so additions require only registry
updates; also consider renaming the helper to
getPreservedFieldsWithoutAutoPopulate and update callers such as
buildInitialMergedResource to use the registry-backed helper.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 08dbfad0-bca4-4c2a-a9fd-de540b28ba2a

📥 Commits

Reviewing files that changed from the base of the PR and between 8a98db8 and b85b593.

📒 Files selected for processing (1)
  • specifyweb/frontend/js_src/lib/components/Merging/autoMerge.ts

@CarolineDenis CarolineDenis self-requested a review May 22, 2026 07:42
@CarolineDenis
Copy link
Copy Markdown
Contributor

@CodeRabbit full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

✅ Actions performed

Full review triggered.

@CarolineDenis CarolineDenis requested a review from a team May 22, 2026 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Dev Attention Needed

Development

Successfully merging this pull request may close these issues.

Merged agents have their Agent Type automatically set to 'Organization' regardless of their individual agent types

5 participants