Storage reorganisation · the plan
The Plan
Separate every stored file into a folder of its own account — in the bucket we already have, under two top-level names it has never used. Nothing is deleted and nothing is moved: every file is copied, and every original stays exactly where it is.
- Buckets
- 1The folders change, not the address
- Trees
- 2img for pictures, doc for everything else
- Steps
- 6Three gates, each a full stop
- Deletions
- 0In every step, at every scale
The problem
Files live in one shared set of flat folders today — inventory/images/,
ezrfp-quote/, profile/ — and the folder name is added by the software at
the moment it is needed. A row stores pump_1788.jpg and nothing more, so every reader
has to already know which folder that name came from. That knowledge is spread across four
codebases and is not written down anywhere the row itself can see.
Two consequences follow. There is no way to answer "which files belong to this account" without consulting the software, and there is no way to move a file without finding every reader that reconstructs its folder by hand.
Make the path itself say who owns the file, and make the stored value the whole answer. After that, one account's files are a folder listing, and a reader needs no knowledge beyond the value it was handed.
The shape we are moving to
Everything stays in the bucket we already have. The first segment is the tree:
| Tree | Holds |
|---|---|
| img | Pictures — asset photos, logos, tag sheets, QR codes |
| doc | Everything else — quotes, contracts, invoices, exports, PDFs |
The second segment says who the file belongs to, and that decides the rest of the shape. The path shape is the access model, not a cosmetic detail:
account <tree>/<capexId>/<domain>/<file> account-user <tree>/<capexId>/<branch>/<userId>/<domain>/<file> user <tree>/<capexId>/user/<domain>/<file> library img/<branch>/<file> doc/<branch>/<domain>/<file> global <tree>/<branch>/<file> belongs to nobody
Worked through, with real values out of clone1:
img/CAP-0YNN74P/assets/2BQISjpe_1782915476233CTS5HSW.jpeg account img/CAP-BBA2A600/user/profile/avatar_1788.png user doc/CAP-NJQJGDJ/copilot-quote/OrderCAPSKOCI7V4DZZE9K.pdf account doc/CAP-BBA2A600/export/1/assets/camp_export_1788.xlsx account-user — user 1 ran it img/model-library/pump_x200.jpg library doc/export/system/nightly_1788.xlsx global
The five rules that make it safe
- One bucket. No new bucket and no rename, so there is never a moment where two buckets have to be kept in step.
- Complete paths in records. The stored value is read as it stands. Nothing prefixes a folder onto it.
- Copy, never move. Every original stays where it is. A half-migrated table reads correctly throughout, and every rollback lands on originals that were never touched.
- Uploads stage in
temp/. A browser upload cannot be written to its final home, because the record referencing it does not exist yet. - The old folders are kept. Nothing is written to them again and nothing is removed from them.
The risky part is not the copying. It is that customers run mobile app versions we cannot update, and those versions ask for the old locations. So the sequence keeps the old locations working for as long as anything asks for them, and evidence — not a date — decides when they can go.
The running order
Six steps and three gates. Steps 1 to 3 are invisible to users; behaviour changes at step 4; nothing is deleted or moved in any of them.
Each step is reversible on its own, and each gate is a full stop rather than a checkpoint. Step by Step takes each one in turn; Validation says where each one actually stands.