Skip to content
Astera Technology

Why must an already named migration be held when its script changes?

Compare the stored script fingerprint before recognising a completed step.

Download complete case notes and answer keys ↓

Read the source and its declared rule

The original fixed local scripts increment quantity by 1 or 2. Applied V2 used increment 1 and recorded its SHA-256. Reusing V2 with increment 2 is a different step content; name-only recognition wrongly treats it as already completed. Corrected ledger compares the actual script hash.

Filled synthetic input
RecordInput JSON
Base input{"version":"V2","script":"UPDATE target SET quantity=quantity+2;"}
Download editable CSV ↓

Compare the actual paths

The changed V2 is held, keeping quantity 2 and the original ledger. Matching V2/content is a recognised repeat; new V3 executes its supplied increment 2 and records its own identity.

Misleading and corrected outputs
PathOutput JSON
Misleading path{"accepted":true,"error":null,"applied":false,"quantity":2,"versions":["V2"]}
Corrected path{"accepted":false,"error":"changed-script","applied":false,"quantity":2,"versions":["V2"]}
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){
    if(!['V2','V3'].includes(i.version)||!['UPDATE target SET quantity=quantity+1;','UPDATE target SET quantity=quantity+2;'].includes(i.script))throw new Error('Outside the fixed local migration scripts');
    const db=new DatabaseSync(':memory:');try{db.exec('CREATE TABLE target(quantity INTEGER);INSERT INTO target VALUES(1);CREATE TABLE ledger(version TEXT PRIMARY KEY,hash TEXT);');
      const first='UPDATE target SET quantity=quantity+1;';const hash=s=>createHash('sha256').update(s).digest('hex');db.exec(first);db.prepare('INSERT INTO ledger VALUES(?,?)').run('V2',hash(first));
      const stored=db.prepare('SELECT hash FROM ledger WHERE version=?').get(i.version);let error=null,applied=false;
      if(stored){if(corrected&&stored.hash!==hash(i.script))error='changed-script';}
      else{db.exec('BEGIN;');db.exec(i.script);db.prepare('INSERT INTO ledger VALUES(?,?)').run(i.version,hash(i.script));db.exec('COMMIT;');applied=true;}
      return {accepted:error===null,error,applied,quantity:db.prepare('SELECT quantity FROM target').get().quantity,versions:rows(db,'SELECT version FROM ledger ORDER BY version').map(r=>r.version)};
    }finally{db.close();}
  };
const cases=[{"label":"misleading base","input":{"version":"V2","script":"UPDATE target SET quantity=quantity+2;"},"expected":{"accepted":true,"error":null,"applied":false,"quantity":2,"versions":["V2"]},"corrected":false},{"label":"corrected base","input":{"version":"V2","script":"UPDATE target SET quantity=quantity+2;"},"expected":{"accepted":false,"error":"changed-script","applied":false,"quantity":2,"versions":["V2"]},"corrected":true},{"label":"same version and exact same script","input":{"version":"V2","script":"UPDATE target SET quantity=quantity+1;"},"expected":{"accepted":true,"error":null,"applied":false,"quantity":2,"versions":["V2"]},"corrected":true},{"label":"new named migration step executes","input":{"version":"V3","script":"UPDATE target SET quantity=quantity+2;"},"expected":{"accepted":true,"error":null,"applied":true,"quantity":4,"versions":["V2","V3"]},"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

Exact same script does not run again. New V3 increases the existing 2 to 4 and records V2, V3.

Editable boundary records and expected outputs
CaseInput JSONExpected output JSON
Same name and exact script{"version":"V2","script":"UPDATE target SET quantity=quantity+1;"}{"accepted":true,"error":null,"applied":false,"quantity":2,"versions":["V2"]}
New named migration content{"version":"V3","script":"UPDATE target SET quantity=quantity+2;"}{"accepted":true,"error":null,"applied":true,"quantity":4,"versions":["V2","V3"]}
Download editable CSV ↓

Does matching version nameV2 establish matching work?

Show the worked answer

No. The increment 2 content differs from the applied increment 1. Hold the changed script underV2; quantity remains 2.

Related resources