Skip to content
Astera Technology

Why did two archive files overwrite one destination?

Group entries by the declared destination-path equivalence while retaining each source label.

Download complete case notes and answer keys ↓

Read the source and its declared rule

The original target uses ASCII path labels and a supplied case-sensitive or case-insensitive equivalence rule. Under case-insensitive mode A.txt and a.txt resolve to the same destination key. Both original entries and their bytes must stay distinguishable; a collision holds the pack instead of selecting the last entry.

Filled synthetic input
RecordInput JSON
Base input{"caseSensitive":false,"entries":[{"name":"A.txt","bytes":[65]},{"name":"a.txt","bytes":[66]}]}
Download editable CSV ↓

Compare the actual paths

The bad destination map keeps only a.txt bytes 66. Corrected output retains A.txt bytes 65 and a.txt bytes 66, records the collision and emits no planned writes.

Misleading and corrected outputs
PathOutput JSON
Misleading path{"accepted":true,"files":[{"destination":"/import/a.txt","source":"a.txt","bytes":[66]}]}
Corrected path{"accepted":false,"sources":[{"name":"A.txt","bytes":[65]},{"name":"a.txt","bytes":[66]}],"collisions":[{"destination":"/import/a.txt","sources":["A.txt","a.txt"]}],"files":[]}
Download editable CSV ↓
Local Node.js program: 22.23.3 or 24.14.1
import {DatabaseSync} from 'node:sqlite';
import {TextDecoder} from 'node:util';
import {inflateSync} from 'node:zlib';
import {posix} from 'node:path';
import {createHash} from 'node:crypto';
const rows=(db,sql)=>db.prepare(sql).all().map(r=>({...r}));
const failed=(action)=>{try{action();return null;}catch(e){return {code:e.errcode??e.code??e.name};}};

import assert from 'node:assert/strict';
const run=function run(i,corrected=true){
    const groups=new Map();const sourceEntries=structuredClone(i.entries);
    for(const e of i.entries){const path=posix.resolve('/import',e.name);const destination=i.caseSensitive?path:path.toLowerCase();
      if(!groups.has(destination))groups.set(destination,[]);groups.get(destination).push(e);}
    if(!corrected)return {accepted:true,files:[...groups].map(([destination,entries])=>{const e=entries.at(-1);return {destination,source:e.name,bytes:e.bytes};})};
    const collisions=[...groups].filter(([,es])=>es.length>1).map(([destination,es])=>({destination,sources:es.map(e=>e.name)}));
    return {accepted:collisions.length===0,sources:sourceEntries,collisions,files:collisions.length?[]:[...groups].map(([destination,es])=>({destination,source:es[0].name,bytes:es[0].bytes}))};
  };
const cases=[{"label":"misleading base","input":{"caseSensitive":false,"entries":[{"name":"A.txt","bytes":[65]},{"name":"a.txt","bytes":[66]}]},"expected":{"accepted":true,"files":[{"destination":"/import/a.txt","source":"a.txt","bytes":[66]}]},"corrected":false},{"label":"corrected base","input":{"caseSensitive":false,"entries":[{"name":"A.txt","bytes":[65]},{"name":"a.txt","bytes":[66]}]},"expected":{"accepted":false,"sources":[{"name":"A.txt","bytes":[65]},{"name":"a.txt","bytes":[66]}],"collisions":[{"destination":"/import/a.txt","sources":["A.txt","a.txt"]}],"files":[]},"corrected":true},{"label":"declared case-sensitive destination","input":{"caseSensitive":true,"entries":[{"name":"A.txt","bytes":[65]},{"name":"a.txt","bytes":[66]}]},"expected":{"accepted":true,"sources":[{"name":"A.txt","bytes":[65]},{"name":"a.txt","bytes":[66]}],"collisions":[],"files":[{"destination":"/import/A.txt","source":"A.txt","bytes":[65]},{"destination":"/import/a.txt","source":"a.txt","bytes":[66]}]},"corrected":true},{"label":"single destination entry","input":{"caseSensitive":false,"entries":[{"name":"A.txt","bytes":[65]}]},"expected":{"accepted":true,"sources":[{"name":"A.txt","bytes":[65]}],"collisions":[],"files":[{"destination":"/import/a.txt","source":"A.txt","bytes":[65]}]},"corrected":true}];
for(const test of cases){
  const actual=run(test.input,test.corrected);
  assert.deepEqual(actual,test.expected,test.label);
  console.log(test.label,actual);
}
Download runnable example ↓

Check boundaries before the next load

Declared case-sensitive mode preserves two destinations. A single entry has no collision in case-insensitive mode.

Editable boundary records and expected outputs
CaseInput JSONExpected output JSON
Case-sensitive destination contract{"caseSensitive":true,"entries":[{"name":"A.txt","bytes":[65]},{"name":"a.txt","bytes":[66]}]}{"accepted":true,"sources":[{"name":"A.txt","bytes":[65]},{"name":"a.txt","bytes":[66]}],"collisions":[],"files":[{"destination":"/import/A.txt","source":"A.txt","bytes":[65]},{"destination":"/import/a.txt","source":"a.txt","bytes":[66]}]}
Single source entry{"caseSensitive":false,"entries":[{"name":"A.txt","bytes":[65]}]}{"accepted":true,"sources":[{"name":"A.txt","bytes":[65]}],"collisions":[],"files":[{"destination":"/import/a.txt","source":"A.txt","bytes":[65]}]}
Download editable CSV ↓

May the last entry silently win under the supplied rule?

Show the worked answer

No. Both source entries are retained and the shared destination is held for an explicit disposition.

Related resources