Migration estimates go wrong when the estate is counted in templates. One application may be a small letter with local wording. Another may pull shared pages, library components, rules, data files, fonts, queues, and language variants through years of accumulated references.
Calling both of them "one application" hides the work.
The first migration deliverable should be a reproducible inventory, not a spreadsheet assembled from memory.
Begin with the repository boundary
Record the repository identity, schema version, database platform, and the mode used for every read. For production scope, approved versions are the normal starting point. Latest work is useful too, but it answers a different question.
List every application with its folder path, version, approval state, and last-edit evidence. Then decide which applications belong to the migration. Do not quietly drop old or apparently dormant applications. Classify them as in scope, retained temporarily, or retirement candidates.
The classification belongs to the program. The inventory supplies evidence for it.
Measure the application closure
For each application, capture the dependency graph and ordered content tree.
A first-pass graph should cover the documents, output queues, data files, directly referenced variables, style sheets, interactive views, author views, and other named dependencies the repository declares.
The deeper content tree should capture the document, page, section, paragraph, and component hierarchy plus deduplicated referenced inventories. It will be much larger, so write it to a file once and query that file rather than rerunning the repository walk for every question.
exstream-design-cli app graph "Customer Letters" \
--profile estate --mode approved --json > customer-letters-graph.json
exstream-design-cli app content-tree "Customer Letters" \
--profile estate --mode approved --max-depth 0 --json \
> customer-letters-tree.json
Repeat the same commands for every run. Deterministic output turns the inventory into a baseline that can be compared later instead of a one-off discovery exercise.
Extract the content that carries migration cost
Object counts are only the shell. The difficult work sits in content and behavior.
Extract customer-facing text in hierarchy order, with language identity and variable references preserved. This supports translation estimates, legal wording review, and content rationalization without flattening a variable reference into its Designer sample value.
exstream-design-cli content extract "Customer Letters" \
--profile estate --mode approved --output csv > customer-letters-text.csv
Inventory formula variables, rules, functions, data-file mappings, and compute timing. These reveal business logic that a page count never will.
Record output queues, output devices, encodings, fonts, paper types, barcodes, images, and external resources. A migration target that reproduces a PDF preview but misses AFP resources, encoding constraints, or postal processing has not reproduced the application.
Keep shared use visible
Shared objects create two opposite risks.
If the inventory counts every placement as a separate object, the estimate is inflated. If it counts a shared object once and forgets that forty applications depend on it, the change risk disappears.
Keep both views:
- One identity per object for migration volume.
- Every placement and reference for impact analysis.
This is particularly important for library components, styles, fonts, variables, and data files. A small shared object can have a very large test radius.
Compare with real packages
The repository says what the design contains. Production runs packages.
Collect the deployed PUB for each active application where possible. Record its hash, creation metadata, Design Manager version, approval selection, and object inventory. Then compare it with the approved repository state.
Differences are migration inputs. A package that does not match the approved repository may represent an emergency patch, an old release, a packaging mistake, or a deliberate operational fork. It needs a disposition before anyone chooses the migration baseline.
Count unknowns as work
A parser that cannot decode a structure should report that boundary. The migration plan should do the same.
Track searchable, partially decoded, and unknown content. Track package generations with partial support. Track references that resolve to no current object. Track assets with bytes but unrecognized metadata.
Unknown does not mean broken. It means the estimate needs a manual inspection, another artifact, or a targeted technical spike.
Hiding unknowns produces a cleaner spreadsheet and a worse migration.
The inventory deliverable
A useful estate inventory contains:
- Repository and extraction provenance.
- Application register with scope decisions.
- Per-application hierarchy and dependency summaries.
- Shared-object identities and usage counts.
- Extracted wording by language and location.
- Logic, data mappings, output configuration, and asset inventories.
- Deployed package identities and repository-to-package differences.
- Coverage, unknowns, and decisions still requiring an owner.
Store the structured files beside a short human report. The report explains the decisions. The files let the team reproduce and update the numbers.
This inventory is useful whichever destination wins. It supports a classic upgrade, a move to cloud-native CE, or a replatforming program. The destination changes. The need to know what you are moving does not.
Discuss an Inspect estate inventory for a repository, a set of PUB packages, or both.