# CSV Import Review: Local Workflow

**Release status, 5 September 2026:** the offline application has passed 41 core/mapping tests and 12 end-to-end Chromium checks, including real file inputs, downloads, malformed input, ambiguous duplicates and an offline export/reopen cycle. Test data is synthetic; no customer delivery or live CRM import is claimed.

## Start With Synthetic Data

Open index.html in a current browser and use the synthetic demo before processing a real export. The application runs locally without CRM credentials, an account or a network connection. A public HTTP demo is for synthetic data only because HTTP delivery can be tampered with; obtain the offline package through a trusted channel and verify its separately published SHA256 before using real data.

Keep the original baseline and incoming files unchanged. Work on copies, and retain the original CRM export as a backup. Do not email production customer data to request configuration help.

## Prepare The Two Inputs

- **Baseline:** the existing records to preserve, such as a CRM export.
- **Incoming:** the new or revised records to compare with that baseline.
- Use comma-delimited CSV files with a header row and one row per record. A spreadsheet workbook must first be exported to CSV; formulas or macros are not part of this workflow. Quoted commas and embedded newlines are supported by the current parser implementation.
- Identify a stable record key present in both files, such as an internal contact ID. A name is usually not a unique identifier. Email-based matching does not prove two records represent the same person.
- Record the input encoding, column names, matching rules and any intended normalization in the agreed specification. The current engine trims header whitespace, preserves cell values as text and matches keys exactly by default. A case-insensitive option is explicit; it does not trim key values or normalize phone numbers. Empty and whitespace-only keys are sent to review. Leading zeros are not intentionally converted to numbers.

The application accepts at most 5 MiB, 10,000 rows and 50 columns per input file. The paid configuration covers one schema with at most 20 columns and 10,000 data rows combined. A real synthetic 10,000-row/20-column Node.js check completed with correct outputs; this is not a mobile performance guarantee.

## Review Before Exporting

1. Load the baseline and incoming copies into the application.
2. Expand Column mapping and verify each incoming-to-target assignment. Initial suggestions use header spelling, punctuation and case only, never business meaning. Choose an existing target or Skip column explicitly. Two incoming columns cannot map to one target. Then select the shared target key. Stop if a required column is missing or an input is reported as malformed.
3. Compare the summary against the input counts and inspect the proposed changes.
4. Review blank-key and duplicate-key rows. Do not choose a winner merely because it appears first or last in a file.
5. Check that blank incoming values do not erase protected baseline values. A blank cell is not an instruction to delete existing data.
6. Inspect the audit and review outputs, then download the relevant files. Retain them together with the input copies and the mapping used.

Only matches under the documented key configuration are eligible for automatic merging. Similar names and uncertain identities need a separate business decision. Correct source copies or the agreed rules, then rerun; do not hide a conflict by deleting it from the review report alone.

The current engine offers two merge modes. **Fill** changes blank baseline fields only; a different nonblank incoming value is reported as a field conflict and the baseline value remains. **Replace** accepts nonblank incoming values and records replacements. In both modes, empty or whitespace-only incoming values cannot erase populated baseline values. The literal text `NULL` is not an empty cell.

Identical repeated rows within an input collapse to one record with an audit entry. When rows sharing a key disagree within either input, all records for that key across both files are quarantined. A field conflict in Fill mode is different: other allowed fields on that same record can still be updated, so review status is not an all-or-nothing row block.

## Outputs

| Output | Purpose | Important limit |
| --- | --- | --- |
| Merged | Accepted-key snapshot with eligible changes | Excludes quarantined keys; NEVER use as an automatic replace-all snapshot |
| Upsert candidates | Eligible new or changed rows for a later import review | Does not perform an import or guarantee CRM-specific upsert semantics |
| Review | Quarantined records and field-level conflicts | Field conflicts may also appear on a record with other accepted changes |
| Audit | Record of decisions and changes produced by the run | Keep with the inputs; do not substitute it for a CRM backup |

The verified download filenames are upserts.csv, accepted-snapshot.csv, review.csv and audit.json. The audit records the mapping and merge rules. Filtering the table does not filter the export; the entire selected result view is downloaded. Changing a rule invalidates old results until Review import is run again. Quarantined baseline rows are absent from the merged output, not deleted from the originals. Keep the baseline and review files: absence from the merged file must never be interpreted as authorization to delete an existing CRM record. Review record numbers count logical CSV records, including the header as record 1; a quoted multiline field is still one record.

## Small Acceptance Fixture

Agree expected results before the paid configuration begins. The bundled synthetic baseline.csv and incoming.csv produce 4 upsert rows, 5 accepted keys, 3 reviewed input records and 2 protected blank overwrites. Replacing incoming.csv with ambiguous.csv produces 1 safe upsert and 4 reviewed records; key 0002 is withheld across both inputs. Actual outputs are included in example-results/.

| Case | Expected decision |
| --- | --- |
| Same unique key; incoming nonblank value changes an allowed field | Propose the permitted update and report the change |
| Same unique key; incoming protected field is blank | Retain the baseline value |
| Unique incoming key absent from baseline | Propose an insertion |
| Two incoming rows share a key but disagree on a value | Quarantine that key across both files |
| Two baseline rows share a key but disagree on a value | Quarantine that key across both files |
| Fully identical repeated rows share a key within one input | Collapse the repetition and record it in the audit |
| Incoming key is missing | Withhold the row for review |
| Existing baseline record has no incoming row | Preserve it; absence is not a deletion instruction |
| Quoted comma or embedded newline in a field | Parse as CSV or show a clear error, never silently shift columns |

Also verify unchanged input files, output column mapping and deterministic repeat runs. Spreadsheet-safe export is enabled by default: selected leading formula characters are escaped with an apostrophe. This intentionally changes exported text, potentially including a key. A raw CRM export may therefore require explicitly disabling that checkbox after review; do not open an untrusted raw export as an executable spreadsheet. The Offline HTML download contains the application and its synthetic demo, not the records or filenames you loaded.

## Before Any CRM Import

Check the destination's current import behavior for record identifiers, blank values, relationships, column types and duplicate handling. Inspect counts and a small representative sample, then use a sandbox or a reversible small import if your CRM supports one. The free application and paid configuration do not authorize access to the CRM or perform this import for you.

After export, close the application tab and manage the downloaded files according to your own data-retention rules. Browser-local processing does not remove files from your Downloads folder, backups or browser extensions, and is not a blanket privacy or compliance guarantee.

Configuration questions: **xvpmjyd4b6@voidmail.ai**. Share the schema, synthetic reproduction and expected result, not passwords or private customer records.
