sf_COSMIC-Measurer is a Python-based measurement toolkit for extracting COSMIC data movements from Salesforce artifacts.
A primary goal of this project is to provide a seamless automated measurement pipeline that integrates directly with the sf_CosmicWorkBench for visualization and record management.
It analyzes metadata and code, classifies movements as E (Entry), R (Read), W (Write), and X (Exit), and outputs structured JSON suitable for posting into a COSMIC data model (for example, cfp_Data_Movements__c pipelines).
It also helps you learn the rules of COSMIC measurement through the Rule Coach.
- Measures Apex classes (
.cls) - Measures Flows (
.flow-meta.xml) - Measures FlexiPages (
.flexipage-meta.xml) - Measures Lightning Web Components (bundle directories with
.js+.html) - Includes a COSMIC Rule Coach skill that answers sizing questions, validates measurer JSON against the source artifact, and looks up rules verbatim from indexed official COSMIC manuals
- Produces deterministic JSON outputs with ordered movements and consistent schema
- Traversal Warning Management: Automatically filters out standard Salesforce framework classes (e.g.,
System,Database,Schema,String) from traversal warnings to ensure focused results. - Supports cross-artifact traversal:
- Flow measurer can resolve and merge invocable Apex movements
- LWC measurer can resolve and merge imported Apex movements
- FlexiPage measurer can resolve tab-bound Flow and LWC candidates
Each movement is one COSMIC Function Point and is represented with:
movementType:E,R,W, orXdataGroupRef: Salesforce object API name (or supported composite form)order: execution ordering within a functional processimplementationType:apex,flow,flexipage,lwc,ootb,config, orlistviewisApiCall: external API interaction flag
Shared reference: .cursor/skills/cosmic-measurer/reference.md
Implementation counting behavior: COUNTING_RULES.md
samples/- sample Apex, Flow, FlexiPage, and LWC inputs used for testing and examplesexpected/- expected JSON fixtures for regression checksmetadata-to-measure/- scratch directory (gitignored) where you drop retrieved Salesforce metadata to measure; clear it out when done.cursor/skills/cosmic-measurer/- artifact-specific measurers and shared modulescosmic-apex-measurer/cosmic-flow-measurer/cosmic-flexipage-measurer/cosmic-lwc-measurer/cosmic-rule-coach/(manual-grounded Q&A, validator, rule lookup, tutor)shared/(common models/output logic)
This repository provides build infrastructure to generate a Cursor plugin bundle. The generated output is intentionally excluded from the repository as it is a build artifact.
- Source of truth:
.cursor/skills/cosmic-measurer/ - Build command:
python3 scripts/build_cursor_plugin.py - Output location:
plugin/cursor-cosmic-measurer/(generated on-demand)
To use the plugin, run the build script to generate the plugin/ directory.
- Python 3.10+ (3.11+ recommended)
pytestfor test executioncoveragefor coverage runs
Minimal install:
python3 -m pip install -U pytest "coverage[toml]"For ad-hoc measurement of metadata retrieved from a Salesforce org, use the metadata-to-measure/ directory at the repo root. It's a gitignored scratch space — drop your .cls, .flow-meta.xml, .flexipage-meta.xml files or LWC bundle directories there, point a measurer at them, and clear the directory out when you're done. This keeps org-specific metadata out of version control while still letting you run the measurers locally.
Run a measurer directly from repo root.
You can also ask the Cursor agent to measure artifacts for you. Use prompts like:
"Please measure with full output to the chat"
For example:
"Please measure
samples/classes/cfp_getDataMovements.clswith full output to the chat"
The agent will use the appropriate measurer skill and display the results directly in the chat.
If you want to understand why the measurer counting things in a certain way, and if it is adherent to the COSMIC method, then you can invoke the COSMIC Rule Coach to explain things to you
python3 .cursor/skills/cosmic-measurer/cosmic-apex-measurer/scripts/measure_apex.py \
samples/classes/cfp_getDataMovements.clsmeasure_apex.py implements high-fidelity parsing:
- Comment Stripping: Automatically removes block and line comments before extraction to avoid false positives.
- Improved Object Resolution: Ignores custom objects and metadata types during class resolution to maintain focus on traversable business logic.
Useful flags:
--json-> print JSON to stdout-o out.json-> write JSON to file--fp-id <Id>-> set functional process id--list-entry-points-> detect multiple entry points--entry-point <param>-> filter to one functional process--search-paths <csv>-> class resolution paths for traversal--no-traverse-> disable called-class traversal
python3 .cursor/skills/cosmic-measurer/cosmic-flow-measurer/scripts/measure_flow.py \
samples/flows/cfp_createCRUDLwithRelatedLists.flow-meta.xmlUseful flags:
--json-o out.json--fp-id <Id>--apex-search-paths <csv>-> resolve invocable Apex classes--no-invocable-apex-> skip invocable Apex traversal
python3 .cursor/skills/cosmic-measurer/cosmic-flexipage-measurer/scripts/measure_flexipage.py \
samples/flexipages/cfp_FunctionalProcess_Record_Page.flexipage-meta.xmlUseful flags:
--json-o out.json--fp-id <Id>--lwc-search-paths <csv>--flow-search-paths <csv>--apex-search-paths <csv>--no-resolve-lwc-candidates--no-resolve-flow-candidates--include-resolution-details-> include diagnostic candidate/resolution arrays in JSON
Command:
python3 .cursor/skills/cosmic-measurer/cosmic-flexipage-measurer/scripts/measure_flexipage.py \
samples/flexipages/cfp_FunctionalProcess_Record_Page.flexipage-meta.xmlcfp_FunctionalProcess_Record_Page.flexipage (FlexiPage)
| Order | Type | Name | Data group | LineNumber | ArtifactName | Via | Merged |
|---|---|---|---|---|---|---|---|
| 11 | E | Receive fpId (Functional Process) | tab:Visualiser | cfp_FunctionalProcess__c | 7 | cfp_getDataMovements.apex | cfp_getDataMovements |
| 12 | R | Read cfp_Data_Movements__c list | tab:Visualiser | cfp_Data_Movements__c | 9 | cfp_getDataMovements.apex | cfp_getDataMovements |
| 13 | X | Display LWC output to user | tab:Visualiser | cfp_Data_Movements__c | — | cfp_FunctionalProcessVisualiser.lwc | — |
| Order | Type | Name | Data group | LineNumber | ArtifactName | Via | Merged |
|---|---|---|---|---|---|---|---|
| 1 | E | Open record page (cfp_FunctionalProcess__c) | cfp_FunctionalProcess__c | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 2 | R | Read page record (cfp_FunctionalProcess__c) | cfp_FunctionalProcess__c | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 3 | X | Display page record (cfp_FunctionalProcess__c) | cfp_FunctionalProcess__c | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 4 | E | Edit page record (cfp_FunctionalProcess__c) | cfp_FunctionalProcess__c | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 5 | W | Write page record (cfp_FunctionalProcess__c) | cfp_FunctionalProcess__c | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 6 | R | Read highlights panel fields (cfp_FunctionalProcess__c) | cfp_FunctionalProcess__c | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 7 | X | Display highlights panel fields (cfp_FunctionalProcess__c) | cfp_FunctionalProcess__c | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 8 | R | Read related list cfp_functionalsteps__c | cfp_functionalsteps__c | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 9 | X | Display related list cfp_functionalsteps__c | cfp_functionalsteps__c | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 10 | X | Inspect LWC cfp_FunctionalProcessVisualiser data movements (TBC) on tab Visualis | tbc | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
| 14 | X | Errors/notifications | status/errors/etc | — | cfp_FunctionalProcess_Record_Page.flexipage | — | — |
Functional size: 3 E + 4 R + 1 W + 6 X = 14 CFP
Notes:
- Artifact traversal: Movements with Via include R/W merged from traversed artifacts.
- Warning: Investigate configured page actions as separate functional processes: Delete, cfp_FunctionalProcess__c.Create_CRUDL, cfp_FunctionalProcess__c.cfp_Add_Email_Notification, cfp_FunctionalProcess__c.cfp_Create_ReadDisplayPair
- Warning: Tab-aware notes: page contains tabs = Listview, metadataView, Visualiser
- Warning: Tab-component bindings: Listview -> aura(lst:dynamicRelatedList), metadataView -> aura(lst:dynamicRelatedList), Visualiser -> lwc(cfp_FunctionalProcessVisualiser)
- Warning: Delegate tab-bound LWCs to lwc-measurer with additional write movement handling: cfp_FunctionalProcessVisualiser
- Canonical exit: Last movement is always X — Errors/notifications (
status/errors/etc), after any artifact-derived exits.
python3 .cursor/skills/cosmic-measurer/cosmic-lwc-measurer/scripts/measure_lwc.py \
--bundle-dir samples/lwc/cfp_FunctionalProcessVisualiserUseful flags:
--json-o out.json--fp-id <Id>--apex-search-paths <csv>--required-type E|R|W|X(repeatable)
The measurers are deterministic extractors — they produce JSON, not judgement. When a sizing decision is ambiguous or when you need to verify that a measurement faithfully reflects both the source artifact and the official COSMIC rules, use the COSMIC Rule Coach skill (.cursor/skills/cosmic-measurer/cosmic-rule-coach/).
The coach operates in four modes, all grounded exclusively in indexed official COSMIC manuals:
- Q&A oracle — answers questions like "Is this a Read or an Entry?"
- Validator — given measurer JSON plus the original source artifact, flags movements that violate manual rules
- Rule lookup — returns a section verbatim with citation
- Tutor — walks through related sections with worked examples from the manuals
Every answer includes verbatim citations back to the indexed manual chunks. When the manuals do not address a question, the coach refuses to interpret rather than hallucinate — false confidence is the worst failure mode for a rule oracle.
The coach is non-functional until the PDF manuals under manuals/ have been indexed. Run once per workspace:
cd .cursor/skills/cosmic-measurer/cosmic-rule-coach
python3 -m scripts.index_manuals manuals manuals-indexedThe indexer is heading-aware and idempotent. Re-running skips manuals whose PDF mtime predates the existing index; delete a manual's subfolder under manuals-indexed/ to force a rebuild.
The coach is not invoked automatically by the measurers — those stay deterministic. Invoke it explicitly when:
- A measurer emits a movement you're unsure about
- You want to validate a measurer's JSON output against its source artifact
- You need to quote a specific COSMIC rule in a review or discussion
- You're learning the COSMIC method and want tutor-style walkthroughs
All measurers emit the same core structure:
{
"functionalProcessId": "<Id>",
"artifact": { "type": "Apex|Flow|FlexiPage|LWC", "name": "..." },
"dataMovements": [
{
"name": "Human-readable movement",
"order": 1,
"movementType": "E|R|W|X",
"dataGroupRef": "ObjectApiName",
"implementationType": "apex|flow|flexipage|lwc|...",
"isApiCall": false
}
]
}By convention, a canonical final exit row is included:
name:Errors/notificationsmovementType:XdataGroupRef:status/errors/etc
For FlexiPage output, dataMovements is the canonical consumer contract. Candidate/resolution arrays (lwcCandidateMeasurements, flowCandidateMeasurements, resolvedLwcMeasurements, resolvedFlowMeasurements) are diagnostics and are only emitted when --include-resolution-details is passed.
sf_COSMIC-Measurer output JSON is compatible with sf_CosmicWorkBench.
The cfp_DataMovementsJSONImporter flow in sf_CosmicWorkBench imports measurements into Salesforce records.
- Generate JSON: Run a measurer (e.g., Apex, Flow, LWC) to create a
.jsonfile in measurements/. - Locate Output: Open the
.jsonfile and copy its entire content. - Run Flow: In Salesforce, navigate to the
sf_CosmicWorkBenchapp or Flow list and run Import Data Movements JSON (cfp_DataMovementsJSONImporter). - Enter Inputs:
- Functional Process Id: Salesforce ID of the target
cfp_Functional_Process__crecord. - JSON Payload: Paste the copied JSON.
- Replace Existing: Toggle to delete previous movements for this process before importing.
- Functional Process Id: Salesforce ID of the target
- Submit: Click Next. The Flow calls Apex
Run_DataMovements_Importerto parse and upsertcfp_Data_Movement__crecords.
sf_COSMIC-Measurer produces:
functionalProcessId: Target ID placeholder.artifact: Source metadata (type/name).dataMovements: List of E/R/X/W movements with line references.traversalWarnings: Resolution issues during measurement.
The workbench importer maps these fields to Salesforce Custom Objects.
Run full project test suite:
python3 -m pytest ".cursor/skills/cosmic-measurer"Per-measurer test runs:
python3 -m pytest ".cursor/skills/cosmic-measurer/cosmic-apex-measurer/tests" -v
python3 -m pytest ".cursor/skills/cosmic-measurer/cosmic-flow-measurer/tests" -v
python3 -m pytest ".cursor/skills/cosmic-measurer/cosmic-flexipage-measurer/tests" -v
python3 -m pytest ".cursor/skills/cosmic-measurer/cosmic-lwc-measurer/tests" -vCoverage examples:
python3 -m pytest ".cursor/skills/cosmic-measurer/cosmic-apex-measurer/tests" --cov=".cursor/skills/cosmic-measurer/cosmic-apex-measurer/scripts" --cov-report=term
python3 -m pytest ".cursor/skills/cosmic-measurer/cosmic-flow-measurer/tests" --cov=".cursor/skills/cosmic-measurer/cosmic-flow-measurer/scripts" --cov-report=term
python3 -m pytest ".cursor/skills/cosmic-measurer/cosmic-flexipage-measurer/tests" --cov=".cursor/skills/cosmic-measurer/cosmic-flexipage-measurer/scripts" --cov-report=term
python3 -m pytest ".cursor/skills/cosmic-measurer/cosmic-lwc-measurer/tests" --cov=".cursor/skills/cosmic-measurer/cosmic-lwc-measurer/scripts" --cov-report=term- Measurers are designed for deterministic output and regression testing via
expected/fixtures. - Some artifact integrations are best-effort (for example unresolved classes are surfaced as warnings, not hard failures).
- Skill definitions in
.cursor/skills/cosmic-measurer/*/SKILL.mddocument each artifact measurer's current scope and rules.