There is no single reliable "unused" flag in a mature Exstream repository.
An object can be absent from one application tree and still be used by another application, an interactive path, a packaging process, an output configuration, a historical package, or an external operational convention. A dynamic name assembled in code may not appear as an ordinary relational reference at all.
An unused-object analysis should therefore produce retirement candidates with evidence and coverage limits. It should never delete them.
Define unused for the decision you are making
"Unused" may mean:
- Not referenced by any approved application.
- Not referenced by approved or latest application states.
- Not present in any currently deployed package.
- Not edited or packaged within a chosen period.
- Not required after a specific migration wave.
These definitions produce different lists. Write the definition at the top of the report before collecting candidates.
For repository cleanup, I would start with the strictest practical scope: no known reference from any approved or latest in-scope application, no presence in deployed packages, and no operational owner claiming the object.
Even then, call it a candidate.
Inventory objects and application closures separately
List every discoverable standalone object by kind from the repository. Then build the dependency graph and content tree for every application in scope.
The object inventory is the population. The union of application closures is the known-used set.
exstream-design-cli object list --kind variable \
--profile estate --mode approved --limit 100000 --json \
> all-approved-variables.json
exstream-design-cli app content-tree "Customer Letters" \
--profile estate --mode approved --max-depth 1 --json \
> customer-letters-tree.json
Repeat for every relevant object family and application. Do not infer estate-wide non-use from one application's tree.
Run both approved and latest modes. A shared component may be absent from approved production but already used by work awaiting approval. Deleting it would turn tomorrow's packaging job into the first dependency test.
Follow more than containment
An application hierarchy is only one reference source.
Include named dependencies and relation inventories for variables, rules, functions, library components, page templates, styles, fonts, data files, output devices, encodings, barcodes, search keys, paper types, and other production objects supported by the repository reader.
Search code and wording where dynamic or indirect use is plausible. A literal object name in a formula, rule, script, control file, or external job can bypass a normal relation table.
Inspect reports known references and explicit decoding boundaries. It should not invent an edge because two names look related. That means a candidate can remain uncertain even after a clean graph walk.
Check deployed packages
The repository may have moved on while production still runs an older package.
Inventory the PUB packages currently deployed, plus packages retained for rollback. Match object identities within the same repository lineage and record package digests.
If an object exists in a deployed or rollback package, repository deletion may still be operationally safe because the package is self-contained. It may also destroy the team's ability to rebuild, inspect, or patch that release. That is a lifecycle decision, not a graph result.
Keep the package evidence in the candidate record.
Use age as context, never proof
Old edit dates and low versions help prioritize review. They do not prove non-use.
The most stable shared resources in an estate may be the oldest objects in it. A font, page template, barcode, output device, or legal paragraph can remain unchanged for years precisely because many applications rely on it.
Likewise, a recent edit does not prove active use. Someone may have touched an abandoned object during investigation.
Use timestamps to order the queue. Use references and ownership to decide it.
Classify every candidate
A useful candidate register has more than yes or no:
used: at least one current application or package proves use.candidate: no known use was found within the declared scope.unknown: a relevant relation or content body could not be decoded or searched.retained: not currently used, but kept for rollback, audit, legal, or planned work.approved for retirement: an owner accepted the evidence and recovery plan.
For each candidate, record object type, name, OI, version, approval state, last edit, searched modes, applications inspected, package presence, coverage gaps, owner, and disposition.
Quarantine before deletion
Use a staged retirement process:
- Freeze the candidate list and capture repository and package baselines.
- Ask application and operational owners to review it.
- Remove or isolate candidates in a non-production repository copy.
- Repackage every affected application.
- Run semantic package comparison and output regression.
- Keep a recoverable export or database backup through the agreed retention period.
- Delete from the live repository only after the evidence and recovery path are accepted.
The exact quarantine mechanism depends on the repository and release process. The principle does not. Make absence reversible before making it permanent.
Keep the final claim within the evidence
CCMForge Inspect can inventory repository objects, walk known application dependencies, search supported content, read deployed packages, and compare before and after states.
It can say "no known reference was found in this declared scope, with these coverage limits."
It should not say "safe to delete" on its own. That conclusion belongs to the team that knows the external jobs, rollback obligations, and business ownership around the repository.