feat(reconcile): per-file snapshot diff of upstream drift since the migration cut - #477
Merged
Merged
Conversation
…igration cut Migrations run for weeks. The Fresh/Deno source repo keeps shipping while the migration team hand-fixes what the codemod got wrong, so upstream changes silently never reach the migrated TanStack repo. New `deco-reconcile` bin: diffs BASE_SHA..SOURCE_HEAD on the source and emits ONE patch per file, plus the context needed to port each one — candidate target paths (basename match over the target tree, ranked by the conventional guess) and, per candidate, the target commits since the migration commit. A non-empty collision list means someone hand-fixed that file: reconcile hunk by hunk instead of applying the upstream change whole. The script makes no judgement and writes nothing to the target. Applying is the agent's job under the new `deco-reconcile-snapshot` skill, which carries the rebase-not-re-migration protocol: learn the translation rules from the cut↔migration commit pair, resolve collisions by asking whether the local fix exists because of the new stack or because of a defect upstream already fixed, sweep applied CSS for the selector-token damage a previous codemod caused, and stop-and-report when collisions exceed one human review sitting. `manifest.json` doubles as resume state (`done` per file). CMS content (`.deco/`) is filtered out — it syncs through its own channel. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…apshot auto-detected Two mandatory SHAs from two different repos was the wrong ergonomics. Now: deco-reconcile --source <fresh> --target <tanstack> --snapshot <cut sha> `--base` → `--snapshot`, `--target-base` → `--target-snapshot`, the latter now optional. Default is NOT target HEAD: `targetSnapshot..HEAD` would then be an empty range and every collision list would come back empty — reading as "nobody hand-fixed anything", which is the single worst thing to get silently wrong here. Instead it detects the commit that added MIGRATION_REPORT.md (the only provenance `deco-migrate` leaves), falls back to HEAD, and prints which commit it picked. detectTargetSnapshot takes the OLDEST add, since git log is newest-first and a re-added report must not shadow the real migration commit. The e2e now drops the flag so auto-detection is on the tested path. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…y SHA First real run (zeedog, 0098f4e..79b54eb) surfaced two defects. 19 files reported, 4 of them noise: deno.json, fresh.gen.ts, manifest.gen.ts and a submodule gitlink. All are paths the migration deletes outright, so they showed up as "no target match — new file?" — an invitation to migrate a file that must not exist on the target. The migrator already owns that list, so this hoists the six delete sets out of phase-analyze.ts into migrate/delete-sets.ts and has both sides read from it. Kept deliberately conservative: routes/ and apps/ are rescaffolded but a NEW upstream route is a real change, and root-level docs are context-dependent, so those rules stay in decideAction. Dropping a genuine upstream change is a worse failure than showing one file too many. Submodules are filtered separately via ls-files mode 160000 — a gitlink bump has no source to port. Second: a file with two target candidates (the island/section re-export pair) counted one hand-fix twice. NewTitleBox read as 5 collisions, actually 3. The inflated number misreads the "does this fit one human review sitting" gate, which is the one call this tool exists to inform. Now deduped by SHA. zeedog re-run: 19 → 15 files, zero false positives, same 10 real collisions. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
JonasJesus42
approved these changes
Aug 18, 2026
|
🎉 This PR is included in version 7.45.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
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.
Problema
Migração Deno/Fresh → TanStack leva semanas. Durante a janela, o repo de origem continua recebendo commits que nunca chegam no repo migrado — e não existia nada que detectasse isso.
migrate.tsé in-place, one-shot, e não grava proveniência (nenhum SHA de origem, nenhum mapeamento source→target persistido).O que entra
deco-reconcile(packages/blocks-cli/scripts/reconcile.ts), bin novo:Diffa
BASE_SHA..SOURCE_HEADna origem e emite um patch por arquivo — segmentar é o ponto, deixa a LLM processar um de cada vez em vez de engolir um patch inteiro. Por arquivo anexa:targetCandidates— basename match sobre ogit ls-filesdo alvo, ranqueado pelo palpite convencional (src/<rel>,islands/→src/components/,static/→public/). Candidato para o agente confirmar, não veredito.collision—git log <target-base>..HEAD -- <candidato>no alvo, com os subjects. Não-vazio = alguém corrigiu à mão ali; reconciliar hunk a hunk em vez de aplicar a mudança upstream inteira.Saída em
<target>/.reconcile/<sourceHead:7>/:manifest.json(que também é o estado de retomada, campodonepor arquivo),INDEX.mdhumano,patches/NNN-<slug>.patch. Zero escrita no alvo, zero julgamento.Ruído filtrado:
.deco/(conteúdo de CMS sincroniza por canal próprio),.github/, lockfiles, binários..agents/skills/deco-reconcile-snapshot/SKILL.md— o protocolo de aplicação: é rebase, não re-migração. Aprender as regras de tradução do par corte↔migração em vez de lista decorada; resolver colisão perguntando se a correção local existe por causa da stack nova ou de um defeito que o upstream já resolveu; varrer o CSS aplicado atrás do dano de token de seletor que um codemod já causou antes; paridade como critério de aceite (bug upstream é portado como está); parar e reportar quando as colisões passarem do que cabe numa sentada de revisão humana.O aplicador é o loop da skill — não há
--apply. Escrever no alvo é julgamento.Decisões
bin, nãoexports— política declarada no"//exports"do package.json.decideAction(migrate/phase-analyze.ts:212), que sabe o mapeamento real: é module-private e exige umFileRecordcompleto (classificação de island, detecção de re-export → leitura e parse de conteúdo). Basename match + convenção já entregam a shortlist e o agente confirma. Se o palpite errar na prática, exportardecideActioné o upgrade natural.Verificação
packages/blocks-cli/scripts/reconcile.test.ts— 5 testes, verde. O e2e monta dois repos git temporários (origem com 2 commits, alvo com commit de migração + uma hand-fix depois) e confere que o lockfile é filtrado, que o patch carrega o hunk certo, e que a hand-fix pós-migração aparece como colisão.Os 6 erros de
typecheckno pacote são pré-existentes (confirmado com stash: 6 antes, 6 depois) e ficam todos emmigrate/*.🤖 Generated with Claude Code
Summary by cubic
Generates a per-file snapshot diff of upstream changes since the migration cut so migrated TanStack repos can safely rebase Fresh/Deno drift. Previously there was no drift detection; now the
deco-reconcileCLI emits one patch per changed file, suggests target candidates, dedupes collision commits, and writes nothing to the target.deco-reconcilebin in@decocms/blocks-cli. Usage:--source <fresh> --target <tanstack> --snapshot <cut-sha>with optional--target-snapshot; output at<target>/.reconcile/<sourceHead:7>/(manifest.jsonwithdone,INDEX.md,patches/*).--basewith--snapshot; replace--target-basewith optional--target-snapshot(auto-detected via the oldest add ofMIGRATION_REPORT.md, else HEAD; the chosen commit is printed)..deco/,.github/,_fresh/, lockfiles, binaries, submodule gitlinks, and now any path the migration deletes (single source of truth inscripts/migrate/delete-sets.tsused by both analyze and reconcile).targetCandidatesvia basename match plus conventional path guesses; collision commits are deduped by SHA across candidates to avoid inflated counts..agents/skills/deco-reconcile-snapshot/SKILL.md(apply protocol: rebase, not re-migration) andreconcile.test.tscovering patch emission, filtering (including delete-sets), collision dedupe, and target snapshot auto-detection.Written for commit c541bf9. Summary will update on new commits.