fix(contact-import): the shipped contacts.csv imports all 50 rows ("HR" maps to the hr department) - #2001
Merged
objectstack-fleet[bot] merged 3 commits intoOct 3, 2026
Conversation
…ption The shipped contacts.csv writes "HR" in its Department column. The import path matches a picklist cell against the option VALUE exactly and the LABEL case-insensitively, so "HR" matched neither `hr` nor "Human Resources" and six of the template's fifty rows failed with invalid_option. The contact mapping now runs Department through a DEPARTMENT_SYNONYMS value map, the mechanism LEAD_SOURCE_SYNONYMS already uses; the template file is unchanged. The _shared.ts header no longer claims values match case-insensitively. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ER8ntXZhYebyQ66aXWdjfT
… mapping The import-mappings suite compared each template's headers with its mapping but never ran a row through it, so six "HR" rows of contacts.csv failed at the import door while the suite stayed green. Each template's rows now go through the import door's own mapping pipeline and cell coercion (@objectstack/core applyMappingToRows + coerceRow) and the test expects no cell error. The header reader shares one quote-aware line splitter with the new row reader. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ER8ntXZhYebyQ66aXWdjfT
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ER8ntXZhYebyQ66aXWdjfT
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This was referenced Oct 3, 2026
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.
Fixes #1998
Clause-②: no. This is an app import mapping; it touches no published schema and no accept set.
What changed
src/sales/mappings/_shared.ts: a newDEPARTMENT_SYNONYMS = { 'HR': 'hr' }sits besideLEAD_SOURCE_SYNONYMS. The file header no longer claims that option values match case-insensitively. In@objectstack/core17.6.0,matchOptioncompares the value exactly and only the label case-insensitively. That is whyHRmatched neither the valuehrnor the label "Human Resources".src/sales/mappings/contact_import.mapping.ts: theDepartmentcolumn now goes throughtransform: 'map'withparams: { valueMap: DEPARTMENT_SYNONYMS }. TheLead Sourcecolumn already uses this mechanism.test/import-mappings.test.ts: one newit.eachover the three shipped templates. Every data row goes through the import door's own steps from@objectstack/core: firstapplyMappingToRowswith the object schema, thenbuildFieldMetaMapandcoerceRow. The test expects no cell error. The header reader and the new row reader now share one quote-aware line splitter..changeset/1998-contacts-template-hr-department.md:'hotcrm': patch.assets/import-templates/contacts.csvis unchanged (git diff f44ab642..HEAD -- assets/is empty). The template is the customer-facing contract that the ruling oncrm_contactmodels a postal address as five flat text fields whilecrm_accountandcrm_leaduseField.address()— and AGENTS.md's own guidance namesField.address()for exactly this #1836 protects.Which synonyms, and why only one
The template's Department column holds 8 distinct values: Executive ×7, Sales ×7, Marketing ×6, Engineering ×6, Support ×6, Finance ×6, HR ×6 and Operations ×6.
crm_contact.departmentdeclares the valuesexecutive,sales,marketing,engineering,support,finance,hrandoperations. Their labels are Executive, Sales, Marketing, Engineering, Support, Finance, Human Resources and Operations. Seven of the template values equal an option label, so the platform's case-insensitive label match lands them. OnlyHRmatches neither a value (an exact match) nor a label. So the map has exactly one entry, and it adds no synonym that no template row uses.Measured through the import door
The app ran under
objectstack start -p 4818on a fresh local database in the worktree.accounts.csvwas imported first (50 of 50 created). Then the shippedcontacts.csvwas sent toPOST /api/v1/data/crm_contact/importwithmappingName: crm_contact_import:f44ab642)b66111ae)runAutomations: false)Before the fix, all six errors were
department invalid_option: Department: "HR" is not a known option, on rows 7, 15, 23, 31, 39 and 47. After the fix, each of those six contacts reads back by email withdepartment: "hr". On the same query, control row 1 (ana.haddad@northwindtraders.example.com) reads back"executive". The served mapping (GET /api/v1/meta/mapping/crm_contact_import) carried{"source":"Department","target":"department","transform":"map","params":{"valueMap":{"HR":"hr"}}}, which equalsdist/objectstack.jsonfrom the same build. Before the fix, the served entry wastransform: "none"with no valueMap.Ablation
The ablation ran from committed
b66111aewithablation-replace.mjs --file src/sales/mappings/_shared.ts --anchor " 'HR': 'hr',\n" --delete, wrapping the vitest run oftest/import-mappings.test.tsunder the shared lock:b8616d6d2395→8ec71c8b8058.Tests 1 failed | 41 passed (42). The failing test isevery cell of 'contacts.csv' coerces through 'crm_contact_import' with no error. It lists exactly the six rows the door reported, fromrow 7 department invalid_option: Department: "HR" is not a known optionthrough row 47.b8616d6d2395) andgit diff HEADis empty.The test reads the mapping from source through
objectstack.config.ts(vitest), not from a build. No dist step sits between the mutation and the run.Verification
pnpm verifyunder the shared lock (slothotcrm-issue-1998) at HEADf10cae68:os-verify-lock: VERDICT command-exit 0sales_home_pagepage:card warning. The i18n gate reports 0 issues, hygiene is clean, the token ratchet is clean and the build passes. Tests:Test Files 176 passed (176),Tests 3790 passed | 1 skipped (3791).Token ratchet,
src/salesrows:other authored metadata 45 1,296 61,985 15,496 —andauthored total 117 10,409 399,528 99,882 107,000other authored metadata 45 1,304 62,174 15,544 —andauthored total 117 10,417 399,717 99,929 107,000Acceptance notes
content/docs/guides/importing-your-data.mdx(How values are matched) andcontent/docs/guides/import-and-export.mdx(Picklists), and in the zh-Hans and zh-Hant copies of each. BymatchOption's source in 17.6.0, a code is compared exactly, so an upper-cased code fails its row. This was not measured at the door beyondHR. It is not changed here because it is outside this card's file surface. It is noted for whoever next edits those guides._shared.tspointsINDUSTRY_SYNONYMSatsrc/objects/_picklists.ts, but the file issrc/sales/objects/_picklists.ts. It is left as is.src/salesinteraction-layer ceiling to about 30,000. That is not done here, and no ceiling is touched.Generated by Claude Code