14 SEPTEMBER 2026 · read from the working tree

Findings

Six things worth knowing before the cron is run, from reading the seeder, the copier and the descriptor list as they stand today. Two are gaps; the rest are either already handled or now closed.

Already handled
2findings 1 and 5
Gaps
2findings 2 and 3
Changed today
2findings 4 and 6
Blocking the run
0none of them
nothing here blocks the run

The two gaps are both about narrowing a seed — running one descriptor at a time, or only a chosen few. A full seed in wave order is available now and produces the ordering you asked for.

The six2 gaps

1. The order you asked for already holdsno change needed

seedAll() runs seedWave(OWN_DESTINATION_DESCRIPTORS) to completion, then seedWave(BORROWED_KEY_DESCRIPTORS). All nine keys you listed are in the first wave; all six camp_inventory_items.additional_details mirrors are in the second. The sequence you described — own the file first, then let the mirror point at that destination — is what the code already does.

2. Within a wave there is no sequencegap, if you need a strict one

seedWave groups descriptors by physical column and runs the groups concurrently, up to pool − 1. So “first equipment categories, then equipment models, then the copilot columns” is not what happens in that literal order — the eight columns start together.

For these nine it does not matter: they are eight different columns and no two of them can resolve the same destination for the same account, so nothing races. But if a run ever needs a genuine one-at-a-time order, that does not exist today.

3. The seed job takes no descriptor selectiongap

FileMigrationSeedJob extends BaseJob<Record<string, never>> and seedAll() takes no arguments. Seeding only the nine keys is therefore not possible without a change.

The other two phases can already be narrowed: FileMigrationRunPayload.descriptorKeys restricts which descriptors a batch is cut from, and the write-back groups its statements by descriptor key. It is the seed alone that is all-or-nothing.

4. The shape-based two-phase split is gonesuperseded

The earlier PLAIN_SHAPES / seedPhases split — plain columns first, document columns second — was replaced upstream by the own/borrow waves. The new rule is strictly stronger: shape was a proxy for ownership and got it wrong in both directions, whereas BORROWED_KEY_DESCRIPTORS names the dependency outright.

5. Duplicate descriptor keys: now zeroclosed

capture_billings.invoice_path and ezestimator_requests.file_path each appeared twice in earlier revisions. Neither does now — every one of the 122 keys is unique.

grep -o "key: '[^']*'" | sort | uniq -d → empty

6. QR codes removed from the migrationdone today

qr_codes.image is out, at your request — 123 descriptors down to 122. It was the only descriptor whose value came from no column at all: nothing stores a QR code’s path, every reader derives it from the id through qrObjectName, and the seeder derived it the same way.

Left alone deliberately: the [BucketFolder.QR, UploadModule.QrCode] pair in FOLDER_MODULES, and everything in upload-targets.ts. Removing the folder pair would put qr on the unmapped list and change how a folder-derived descriptor treats a qr/ path it happens to meet.