Extracts COSMIC data movements (E/R/X/W) from Apex classes and outputs JSON for posting to a COSMIC database. Use when measuring Apex classes for functional size, analyzing .cls files for data movements, or generating cfp_Data_Movements__c from Apex code.
Produce a COSMIC measurement for one Apex class: classify data movements as Entry, Read, Write, or Exit, assign order, and emit JSON matching reference.md for attachment to a functional process.
Inspects Apex .cls files, identifies data movements, and produces JSON in the format defined in reference.md.
/* ... */) and line (//) comments before analysis to ensure accuracy.ClassName.methodName) are traversed when is found in search paths; R/W from callees are merged into the FP. Standard Salesforce framework classes (e.g., , , , ) are filtered out to reduce noise.ClassName.clsSystemDatabaseSchemaString__c) and metadata types (__mdt) are ignored during class resolution in static calls.@AuraEnabled, @InvocableMethod, or standard entry pointsId for a specific object → use that object; generic Id → use object from usage context (e.g. fpId used in cfp_FunctionalProcess__c = :fpId → cfp_FunctionalProcess__c)apex[SELECT ... FROM ObjectName ...] — extract object from FROM clauseDatabase.query(string), Database.getQueryLocator(string) — parse string for object name if staticFROM RecordType resolves record type Ids (metadata/setup). Do not count these as functional-process data movements — they are excluded from dataMovements and the deterministic script surfaces them under Notes (and optional JSON recordTypeReadsExcludedFromCfp). Other objects queried with RecordTypeId = :binding or RecordType.DeveloperName filters still map to their business object (e.g. Asset::Location) and are counted as usual.apexfalse unless @future(callout=true) or explicit HTTP calloutinsert, update, upsert, delete — extract object from first argument typeDatabase.insert(), Database.update(), etc.apexdataGroupRef, including composite Asset::Location vs Asset::Component) count as one Write. COSMIC measures functional size; insert vs update is an implementation detail of a single reconciliation/sync.return statements → Exit rows with inferred dataGroupRef (typed collections, etc.), as today.Errors/notifications, last in order. dataGroupRef: status/errors/etc (see reference.md). The deterministic script adds this row in build_output; it does not replace parser-derived exits.apex for the canonical exit when measuring Apex artifactsmeasure_apex.py path/to/Class.cls --list-entry-points. If the output shows more than one entry point (e.g. facilityIds, surveyIds), ask the user which entry point to measure before proceeding.--entry-point PARAM if the user chose one; otherwise run normally..cls file (stripping comments).@AuraEnabled, @InvocableMethod, constructor, or static factory)A Python implementation provides deterministic output for automation and regression:
# List entry points (for multi-process detection)
python3 .cursor/skills/cosmic-measurer/cosmic-apex-measurer/scripts/measure_apex.py path/to/Class.cls --list-entry-points
# Measure (optionally filter to one entry point)
python3 .cursor/skills/cosmic-measurer/cosmic-apex-measurer/scripts/measure_apex.py path/to/Class.cls [-o output.json] [--fp-id 001xxx] [--entry-point facilityIds]
# Traversal options (default: traverse into called classes when .cls found)
python3 ... measure_apex.py Class.cls [--search-paths samples,force-app/main/default/classes,src/classes] [--no-traverse]
Call-chain traversal: By default, static calls (ClassName.methodName) are resolved by globbing for ClassName.cls in --search-paths. If found, R/W from the callee are merged; if not found, the class is listed in calledClassesNotFound. Use --no-traverse to measure only the primary class.
Multi-process classes: When a class has multiple entry points (e.g. constructor with facilityIds and static factory forSurveys(surveyIds)), always run --list-entry-points first. If multiple are found, ask the user which to measure, then run with --entry-point <param>.
Run tests:
python3 .cursor/skills/cosmic-measurer/cosmic-apex-measurer/tests/test_measure_apex.py
movementType is exactly E, R, X, or W; dataGroupRef uses object API names and optional ::RecordTypeDeveloperName / ::unknown RT suffixes as in reference.md.status/errors/etc); CFP includes parser exits plus this X.--list-entry-points shows more than one candidate, do not silently pick one — confirm with the user.W in dataMovements when applying the merge rules above.When reporting measurement results to the user (from script output or synthesized manually):
order, movementType, name, dataGroupRef, implementationType, artifactName, isApiCall).dataMovements).return exits)viaClass from traversed .cls calleescalledClassesNotFound lists Apex classes called via static method but not found under --search-paths (system types are filtered out)FROM RecordType, list line(s) and names here; they are not rows in dataMovements (see Read (R))The CLI prints this automatically: default table mode appends the summary after the table; -o file.json writes JSON and prints the same summary to stdout. --json to stdout is machine-only (no appended summary, to keep stdout parseable).
Produce JSON matching the schema in reference.md:
{
"functionalProcessId": "<placeholder or user-provided>",
"artifact": { "type": "Apex", "name": "ClassName.apex" },
"dataMovements": [
{ "name": "...", "order": 1, "movementType": "E", "dataGroupRef": "...", "implementationType": "apex", "isApiCall": false },
{ "name": "Upsert Application_Log__c records", "movementType": "W", "viaClass": "ApplicationLogHandler", ... },
{ "name": "Return SomeObject__c list", "movementType": "X", "dataGroupRef": "SomeObject__c", ... },
{ "name": "Errors/notifications", "order": 4, "movementType": "X", "dataGroupRef": "status/errors/etc", "implementationType": "apex", "isApiCall": false }
],
"calledClassesNotFound": ["Database", "String", "SomeExternalUtil"],
"recordTypeReadsExcludedFromCfp": [
{ "name": "Read RecordType list", "sourceLine": 47 }
]
}
FROM RecordType was detected; omitted when none. Not part of CFP row count..cls in search paths). This list automatically filters out standard Salesforce framework classes and custom object/metadata types to ensure only relevant business logic dependencies are reported.