Vai al contenuto

Sincronizzazione editoriale D1

Scopo e confine

Questo runbook copia in modo esplicito il contenuto editoriale canonico dal D1 di produzione al D1 locale persistito oppure, in una fase separata, al D1 remoto architecture-coffee-development-refresh-20260822. La produzione è sempre una sorgente in sola lettura: il comando repository-owned esegue una singola SELECT costruita dal codice, tramite il Wrangler installato nel repository. Non esiste un'opzione per scegliere un database o un ambiente arbitrario e la produzione non è mai accettata come target.

La sincronizzazione non è replica continua, non collega l'applicazione locale a produzione, non applica migrazioni remote e non distribuisce Worker.

Dataset ammesso

Lo snapshot contiene esattamente:

  • editorial_topics;
  • knowledge_assets;
  • glossary_terms;
  • issues.

Per queste tabelle sono ammessi solo i nomi di colonna dichiarati nello script. ID, slug, JSON canonico, stati, revisioni, relazioni e timestamp vengono conservati. issue_number_sequence non viene letto dalla produzione: dopo l'import viene riconciliato dal massimo numero Issue presente nel target, senza ridurre un valore locale già più alto.

Le relazioni nei campi JSON vengono preservate come insiemi ordinati di identificatori canonici e validate per forma e unicità. Lo schema corrente non impone che ogni identificatore JSON risolva una riga presente nello stesso snapshot; i vincoli SQL e il controllo foreign key restano l'autorità per l'integrità referenziale del database.

Sono esclusi feedback, Campaign Draft e attempt, stato Mailjet/provider, job e diagnostica di generazione, operation e attempt di deployment, autenticazione, recipient/consenso, code ed evidenza operativa. issue_draft_revisions è esclusa perché l'Admin corrente legge issues.draft_revision e crea nuove revisioni, ma non espone una lettura della storia. La tabella esclusa contiene inoltre contenuto precedente e change_reason non necessari per il test locale.

La sostituzione si ferma prima della mutazione se la rimozione di una Issue solo locale produrrebbe una cascata su una tabella esclusa. I conteggi delle tabelle escluse vengono confrontati prima e dopo ogni import.

Prerequisiti

  1. Usare Node.js 22 o successivo con nvm use ed eseguire npm ci.
  2. Verificare che il Wrangler repository-owned sia v4 con ./node_modules/.bin/wrangler --version.
  3. Per letture remote, autenticare Wrangler con l'account autorizzato.
  4. Preparare lo schema locale con npm run db:prepare:local oppure applicare tutte le migrazioni a un percorso persistito isolato.
  5. Non eseguire migrazioni, deployment o operazioni provider durante la stessa finestra.

Lo script richiede che la storia d1_migrations e lo schema delle colonne ammesse coincidano esattamente con il repository. Qualsiasi drift interrompe il flusso.

Snapshot di produzione in sola lettura

Per conservare esplicitamente uno snapshot locale:

npm run db:snapshot-editorial:production -- \
  --retain-to .d1-backups/production-editorial-YYYYMMDD.json

Il file deve restare sotto .d1-backups/, directory ignorata da Git, e viene creato senza sovrascrivere file esistenti con permessi 0600 dove supportati. L'output terminale contiene solo conteggi, distribuzioni di stato, checksum SHA-256 e percorso locale; non contiene righe editoriali. Non allegare mai il file a Issue, PR, workflow o artifact CI.

La sincronizzazione composta usa invece un file temporaneo 0600 e lo elimina su successo o errore.

Import e sincronizzazione locale

merge aggiorna o inserisce le righe di produzione per chiave stabile e lascia le righe solo locali. replace rimuove esclusivamente le righe canoniche solo locali e poi importa lo snapshot; non cancella mai implicitamente feedback, audit, job, Campaign Draft o altre tabelle escluse. La modalità è obbligatoria: non esiste un default distruttivo.

Preflight senza mutazione:

npm run db:import-editorial:local -- \
  --snapshot .d1-backups/production-editorial-YYYYMMDD.json \
  --mode replace \
  --dry-run

Import di uno snapshot già conservato:

npm run db:import-editorial:local -- \
  --snapshot .d1-backups/production-editorial-YYYYMMDD.json \
  --mode replace

Snapshot, import e validazione composti con cleanup automatico:

npm run db:sync-editorial:local -- --mode merge --dry-run
npm run db:sync-editorial:local -- --mode merge

Per una prova isolata è ammesso --persist-to solo sotto .wrangler/state/ o una sottodirectory della directory temporanea di sistema. Il percorso locale ordinario resta .wrangler/state/development.

Backup e preflight dello sviluppo remoto

Il backup editoriale dello sviluppo contiene le quattro tabelle ammesse e il suo issue_number_sequence; non copia tabelle operative escluse che il piano di import non deve modificare:

npm run db:backup-editorial:development -- \
  --retain-to .d1-backups/development-editorial-before-YYYYMMDD.json

Il preflight composto legge produzione e sviluppo, valida schema, migrazioni, checksum, JSON, relazioni, chiavi, foreign key e piano di sostituzione, ma non scrive nel D1 remoto:

npm run db:sync-editorial:development -- --dry-run

Una mutazione dello sviluppo remoto è una fase distinta. Richiede scope esplicito nell'Issue mergiata da Fra, un backup locale nuovo e il token esatto architecture-coffee-development-refresh-20260822:

npm run db:sync-editorial:development -- \
  --confirm architecture-coffee-development-refresh-20260822 \
  --backup-to .d1-backups/development-editorial-before-YYYYMMDD-HHMM.json

In alternativa, con uno snapshot di produzione già conservato:

npm run db:import-editorial:development -- \
  --snapshot .d1-backups/production-editorial-YYYYMMDD.json \
  --confirm architecture-coffee-development-refresh-20260822 \
  --backup-to .d1-backups/development-editorial-before-YYYYMMDD-HHMM.json

Lo sviluppo remoto usa solo replace. Il token errato, assente o riferito alla produzione viene rifiutato. L'implementazione e la review del comando non autorizzano la sua esecuzione.

Validazioni e criteri di arresto

Prima della scrittura vengono controllati formato e checksum, sorgente esatta, tabelle e colonne, migrazioni, JSON, ID, slug, stati, revisioni, relazioni, conflitti di chiave e dipendenze escluse. Il SQL target viene generato dal repository e rifiuta DDL, trigger, migrazioni, PRAGMA, transazioni e target non ammessi.

Dopo la scrittura vengono riletti il dataset e issue_number_sequence, viene eseguito il controllo foreign key e vengono confrontati i conteggi esclusi. In replace la proiezione deve coincidere con lo snapshot; in merge ogni riga dello snapshot deve coincidere e le righe solo locali restano presenti. Una seconda esecuzione deve conservare lo stesso stato e non creare duplicati.

Fermarsi senza tentare correzioni automatiche quando:

  • schema o migrazioni non coincidono;
  • checksum, JSON, relazione o stato non è valido;
  • esiste un conflitto fra ID, slug o numero Issue;
  • replace toccherebbe una dipendenza esclusa;
  • Wrangler non è v4, il target non è esatto o il token non coincide;
  • una lettura, un import o il readback è fallito o ambiguo;
  • cambiano i conteggi delle tabelle escluse o una foreign key non è valida.

Cleanup e recupero

I file temporanei composti vengono eliminati anche in caso di errore. Eliminare uno snapshot conservato solo dopo aver concluso le verifiche, indicando il file esatto sotto .d1-backups/; non cancellare l'intera directory alla cieca.

Un errore locale prima della mutazione non richiede recupero. Dopo un import locale non valido, fermare API e MCP e scegliere esplicitamente fra nuovo import dello snapshot o npm run db:reset:local seguito da npm run db:prepare:local; il reset elimina tutto il D1 locale.

Dopo una mutazione remota fallita o un readback ambiguo, conservare il backup, non ripetere il comando e non improvvisare SQL di riparazione. Il ripristino del D1 di sviluppo usa il rollback già approvato nell'Issue; se manca o lo stato non è determinabile, applica la stop condition eccezionale senza un retry.

Evidenza sanitizzata

Issue e PR possono riportare solo comando, esito, conteggi per tabella, distribuzioni di stato, checksum, controllo foreign key, sequenza riconciliata e lista delle tabelle escluse. Non riportare righe, note, identità reviewer, payload JSON, identificatori infrastrutturali, URL firmati o altri dati privati.