diff --git a/bitmap2component/bitmap2cmp_main.cpp b/bitmap2component/bitmap2cmp_main.cpp
index 05cf3df553..3fe594d9ef 100644
--- a/bitmap2component/bitmap2cmp_main.cpp
+++ b/bitmap2component/bitmap2cmp_main.cpp
@@ -34,7 +34,7 @@ namespace BMP2CMP {
 
 static struct IFACE : public KIFACE_BASE
 {
-    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
+    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
 
     wxWindow* CreateKiWindow( wxWindow* aParent, int aClassId, KIWAY* aKiway,
                             int aCtlBits = 0 ) override
@@ -103,7 +103,7 @@ PGM_BASE* PgmOrNull()
 #endif
 
 
-bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
+bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
 {
     return start_common( aCtlBits );
 }
diff --git a/common/jobs/job_dispatcher.cpp b/common/jobs/job_dispatcher.cpp
index 65b92602d8..0713ac32dd 100644
--- a/common/jobs/job_dispatcher.cpp
+++ b/common/jobs/job_dispatcher.cpp
@@ -24,7 +24,8 @@
 #include <wx/debug.h>
 
 
-JOB_DISPATCHER::JOB_DISPATCHER()
+JOB_DISPATCHER::JOB_DISPATCHER( KIWAY* aKiway ) :
+        m_kiway( aKiway )
 {
     m_reporter = &NULL_REPORTER::GetInstance();
     m_progressReporter = nullptr;
diff --git a/common/jobs/job_dispatcher.h b/common/jobs/job_dispatcher.h
index f9d3fcf16e..45eef27a45 100644
--- a/common/jobs/job_dispatcher.h
+++ b/common/jobs/job_dispatcher.h
@@ -27,21 +27,23 @@
 #include <jobs/job.h>
 
 
+class KIWAY;
 class REPORTER;
 class PROGRESS_REPORTER;
 
 class JOB_DISPATCHER
 {
 public:
-    JOB_DISPATCHER();
+    JOB_DISPATCHER( KIWAY* aKiway );
     void Register( const std::string& aJobTypeName, std::function<int( JOB* job )> aHandler );
     int  RunJob( JOB* job );
     void SetReporter( REPORTER* aReporter );
     void SetProgressReporter( PROGRESS_REPORTER* aReporter );
 
 protected:
-    REPORTER* m_reporter; // non-owning
-    PROGRESS_REPORTER* m_progressReporter; // non-owning
+    KIWAY*             m_kiway;             // non-owning
+    REPORTER*          m_reporter;          // non-owning
+    PROGRESS_REPORTER* m_progressReporter;  // non-owning
 
 private:
     std::map<std::string, std::function<int( JOB* job )>> m_jobHandlers;
diff --git a/common/jobs/job_pcb_drc.cpp b/common/jobs/job_pcb_drc.cpp
index 66bb56506e..5c54c14810 100644
--- a/common/jobs/job_pcb_drc.cpp
+++ b/common/jobs/job_pcb_drc.cpp
@@ -29,6 +29,6 @@ JOB_PCB_DRC::JOB_PCB_DRC( bool aIsCli ) :
     m_severity( RPT_SEVERITY_ERROR | RPT_SEVERITY_WARNING ),
     m_format( OUTPUT_FORMAT::REPORT ),
     m_exitCodeViolations( false ),
-    m_parity( false )
+    m_parity( true )
 {
 }
\ No newline at end of file
diff --git a/common/kiway.cpp b/common/kiway.cpp
index dd579ebc9c..6b02f347b8 100644
--- a/common/kiway.cpp
+++ b/common/kiway.cpp
@@ -311,7 +311,7 @@ KIFACE* KIWAY::KiFACE( FACE_T aFaceId, bool doLoad )
 
             try
             {
-                startSuccess = kiface->OnKifaceStart( m_program, m_ctl );
+                startSuccess = kiface->OnKifaceStart( m_program, m_ctl, this );
             }
             catch (...)
             {
diff --git a/cvpcb/cvpcb.cpp b/cvpcb/cvpcb.cpp
index f285d48310..6e83e92429 100644
--- a/cvpcb/cvpcb.cpp
+++ b/cvpcb/cvpcb.cpp
@@ -34,9 +34,41 @@
 #include <settings/cvpcb_settings.h>
 #include <display_footprints_frame.h>
 #include <kiface_ids.h>
+#include <project_pcb.h>
 
 namespace CV {
 
+int testFootprintLink( const wxString& aFootprint, PROJECT* aProject )
+{
+    FP_LIB_TABLE*           libTable = PROJECT_PCB::PcbFootprintLibs( aProject );
+    const FP_LIB_TABLE_ROW* libTableRow = nullptr;
+    LIB_ID                  fpID;
+
+    fpID.Parse( aFootprint );
+
+    wxString libName = fpID.GetLibNickname();
+    wxString fpName = fpID.GetLibItemName();
+
+    try
+    {
+        libTableRow = libTable->FindRow( libName );
+    }
+    catch( const IO_ERROR& )
+    {
+        // Error state processed below
+    }
+
+    if( !libTableRow )
+        return KIFACE_TEST_FOOTPRINT_LINK_NO_LIBRARY;
+    else if( !libTable->HasLibrary( libName, true ) )
+        return KIFACE_TEST_FOOTPRINT_LINK_LIBRARY_NOT_ENABLED;
+    else if( !libTable->FootprintExists( libName, fpName ) )
+        return KIFACE_TEST_FOOTPRINT_LINK_NO_FOOTPRINT;
+    else
+        return 0;
+}
+
+
 static struct IFACE : public KIFACE_BASE
 {
     // Of course all are virtual overloads, implementations of the KIFACE.
@@ -45,7 +77,7 @@ static struct IFACE : public KIFACE_BASE
             KIFACE_BASE( aName, aType )
     {}
 
-    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
+    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
 
     void OnKifaceEnd() override;
 
@@ -86,6 +118,9 @@ static struct IFACE : public KIFACE_BASE
         case KIFACE_GLOBAL_FOOTPRINT_TABLE:
             return (void*) &GFootprintTable;
 
+        case KIFACE_TEST_FOOTPRINT_LINK:
+            return (void*) testFootprintLink;
+
         default:
             return nullptr;
         }
@@ -145,7 +180,7 @@ FOOTPRINT_LIST_IMPL GFootprintList;
 // A short lived implementation.  cvpcb will get combine into Pcbnew shortly, so
 // we skip setting KICAD7_FOOTPRINT_DIR here for now.  User should set the environment
 // variable.
-bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
+bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
 {
     // This is process level, not project level, initialization of the DSO.
 
diff --git a/eeschema/dialogs/dialog_erc.cpp b/eeschema/dialogs/dialog_erc.cpp
index 47d02d0965..4cdca12eb3 100644
--- a/eeschema/dialogs/dialog_erc.cpp
+++ b/eeschema/dialogs/dialog_erc.cpp
@@ -488,7 +488,8 @@ void DIALOG_ERC::testErc()
 
     {
         wxBusyCursor dummy;
-        tester.RunTests( m_parent->GetCanvas()->GetView()->GetDrawingSheet(), m_parent, this );
+        tester.RunTests( m_parent->GetCanvas()->GetView()->GetDrawingSheet(), m_parent,
+                         m_parent->Kiway().KiFACE( KIWAY::FACE_CVPCB ), &m_parent->Prj(), this );
     }
 
     // Update marker list:
diff --git a/eeschema/eeschema.cpp b/eeschema/eeschema.cpp
index 5dc0eb9ef5..eb4b6e5e9d 100644
--- a/eeschema/eeschema.cpp
+++ b/eeschema/eeschema.cpp
@@ -117,7 +117,7 @@ static std::unique_ptr<SCHEMATIC> readSchematicFromFile( const std::string& aFil
 }
 
 
-bool generateSchematicNetlist( const wxString& aFilename, wxString& aNetlist )
+bool generateSchematicNetlist( const wxString& aFilename, std::string& aNetlist )
 {
     std::unique_ptr<SCHEMATIC> schematic = readSchematicFromFile( aFilename.ToStdString() );
     NETLIST_EXPORTER_KICAD exporter( schematic.get() );
@@ -139,7 +139,7 @@ static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
             UNITS_PROVIDER( schIUScale, EDA_UNITS::MILLIMETRES )
     {}
 
-    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
+    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
 
     void Reset() override;
 
@@ -373,7 +373,7 @@ PGM_BASE* PgmOrNull()
 }
 
 
-bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
+bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
 {
     // This is process-level-initialization, not project-level-initialization of the DSO.
     // Do nothing in here pertinent to a project!
@@ -394,7 +394,7 @@ bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
     if( !loadGlobalLibTable() )
         return false;
 
-    m_jobHandler = std::make_unique<EESCHEMA_JOBS_HANDLER>();
+    m_jobHandler = std::make_unique<EESCHEMA_JOBS_HANDLER>( aKiway );
 
     if( m_start_flags & KFCTL_CLI )
     {
diff --git a/eeschema/eeschema_jobs_handler.cpp b/eeschema/eeschema_jobs_handler.cpp
index 2d0b7d3ea7..e64079ee6b 100644
--- a/eeschema/eeschema_jobs_handler.cpp
+++ b/eeschema/eeschema_jobs_handler.cpp
@@ -37,6 +37,7 @@
 #include <memory>
 #include <connection_graph.h>
 #include "eeschema_helpers.h"
+#include <kiway.h>
 #include <sch_painter.h>
 #include <locale_io.h>
 #include <erc.h>
@@ -64,7 +65,8 @@
 #include <fields_data_model.h>
 
 
-EESCHEMA_JOBS_HANDLER::EESCHEMA_JOBS_HANDLER()
+EESCHEMA_JOBS_HANDLER::EESCHEMA_JOBS_HANDLER( KIWAY* aKiway ) :
+        JOB_DISPATCHER( aKiway )
 {
     Register( "bom",
               std::bind( &EESCHEMA_JOBS_HANDLER::JobExportBom, this, std::placeholders::_1 ) );
@@ -981,7 +983,8 @@ int EESCHEMA_JOBS_HANDLER::JobSchErc( JOB* aJob )
     m_reporter->Report( _( "Running ERC...\n" ), RPT_SEVERITY_INFO );
 
     std::unique_ptr<DS_PROXY_VIEW_ITEM> drawingSheet( getDrawingSheetProxyView( sch ) );
-    ercTester.RunTests( drawingSheet.get(), nullptr, m_progressReporter );
+    ercTester.RunTests( drawingSheet.get(), nullptr, m_kiway->KiFACE( KIWAY::FACE_CVPCB ),
+                        &sch->Prj(), m_progressReporter );
 
     markersProvider->SetSeverities( ercJob->m_severity );
 
diff --git a/eeschema/eeschema_jobs_handler.h b/eeschema/eeschema_jobs_handler.h
index fc3760961f..08990d61ea 100644
--- a/eeschema/eeschema_jobs_handler.h
+++ b/eeschema/eeschema_jobs_handler.h
@@ -29,6 +29,7 @@ namespace KIGFX
 class SCH_RENDER_SETTINGS;
 };
 
+class KIWAY;
 class SCHEMATIC;
 class JOB_SYM_EXPORT_SVG;
 class LIB_SYMBOL;
@@ -40,7 +41,7 @@ class DS_PROXY_VIEW_ITEM;
 class EESCHEMA_JOBS_HANDLER : public JOB_DISPATCHER
 {
 public:
-    EESCHEMA_JOBS_HANDLER();
+    EESCHEMA_JOBS_HANDLER( KIWAY* aKiway );
     int JobExportBom( JOB* aJob );
     int JobExportPythonBom( JOB* aJob );
     int JobExportNetlist( JOB* aJob );
diff --git a/eeschema/erc.cpp b/eeschema/erc.cpp
index fa3513a0e1..dafc3f8eaf 100644
--- a/eeschema/erc.cpp
+++ b/eeschema/erc.cpp
@@ -50,6 +50,7 @@
 #include <wx/ffile.h>
 #include <sim/sim_lib_mgr.h>
 #include <progress_reporter.h>
+#include <kiway.h>
 
 
 /* ERC tests :
@@ -1136,7 +1137,7 @@ int ERC_TESTER::TestSimModelIssues()
 
 
 void ERC_TESTER::RunTests( DS_PROXY_VIEW_ITEM* aDrawingSheet, SCH_EDIT_FRAME* aEditFrame,
-                           PROGRESS_REPORTER* aProgressReporter )
+                           KIFACE* aCvPcb, PROJECT* aProject, PROGRESS_REPORTER* aProgressReporter )
 {
     ERC_SETTINGS& settings = m_schematic->ErcSettings();
 
diff --git a/eeschema/erc.h b/eeschema/erc.h
index cec021be5a..84eb41b9c8 100644
--- a/eeschema/erc.h
+++ b/eeschema/erc.h
@@ -23,19 +23,19 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#ifndef _ERC_H
-#define _ERC_H
+#ifndef ERC_H
+#define ERC_H
 
 #include <erc_settings.h>
 
 
-class NETLIST_OBJECT;
-class NETLIST_OBJECT_LIST;
 class SCH_SHEET_LIST;
 class SCHEMATIC;
 class DS_PROXY_VIEW_ITEM;
 class SCH_EDIT_FRAME;
 class PROGRESS_REPORTER;
+struct KIFACE;
+class PROJECT;
 
 
 extern const wxString CommentERC_H[];
@@ -136,7 +136,7 @@ public:
     int TestMissingNetclasses();
 
     void RunTests( DS_PROXY_VIEW_ITEM* aDrawingSheet, SCH_EDIT_FRAME* aEditFrame,
-                   PROGRESS_REPORTER* aProgressReporter );
+                   KIFACE* aCvPcb, PROJECT* aProject, PROGRESS_REPORTER* aProgressReporter );
 
 private:
 
@@ -144,4 +144,4 @@ private:
 };
 
 
-#endif  // _ERC_H
+#endif  // ERC_H
diff --git a/eeschema/erc_settings.h b/eeschema/erc_settings.h
index 182055f0da..133ea826aa 100644
--- a/eeschema/erc_settings.h
+++ b/eeschema/erc_settings.h
@@ -41,30 +41,31 @@ enum ERCE_T
     ERCE_ENDPOINT_OFF_GRID,       ///< Pin or wire-end off grid.
     ERCE_PIN_NOT_CONNECTED,       ///< Pin not connected and not no connect symbol.
     ERCE_PIN_NOT_DRIVEN,          ///< Pin connected to some others pins but no pin to drive it.
-                                  ///< pins to drive it can be output, passive, 3sttae, I/O
+                                  ///<   pins to drive it can be output, passive, 3sttae, I/O
     ERCE_POWERPIN_NOT_DRIVEN,     ///< Power input pin connected to some others pins but no
-                                  ///< power out pin to drive it.
+                                  ///<   power out pin to drive it.
     ERCE_HIERACHICAL_LABEL,       ///< Mismatch between hierarchical labels and pins sheets.
     ERCE_NOCONNECT_CONNECTED,     ///< A no connect symbol is connected to more than 1 pin.
     ERCE_NOCONNECT_NOT_CONNECTED, ///< A no connect symbol is not connected to anything.
     ERCE_LABEL_NOT_CONNECTED,     ///< Label not connected to anything.
     ERCE_SIMILAR_LABELS,          ///< 2 labels are equal for case insensitive comparisons.
     ERCE_DIFFERENT_UNIT_FP,       ///< Different units of the same symbol have different
-                                  ///< footprints assigned.
-    ERCE_MISSING_POWER_INPUT_PIN, ///< Symbol has power input pins that are not placed on the schematic
+                                  ///<   footprints assigned.
+    ERCE_MISSING_POWER_INPUT_PIN, ///< Symbol has power input pins that are not placed on the
+                                  ///<   schematic
     ERCE_MISSING_INPUT_PIN,       ///< Symbol has input pins that are not placed
     ERCE_MISSING_BIDI_PIN,        ///< Symbol has bi-directional pins that are not placed
     ERCE_MISSING_UNIT,            ///< Symbol has units that are not placed on the schematic
     ERCE_DIFFERENT_UNIT_NET,      ///< Shared pin in a multi-unit symbol is connected to
-                                  ///< more than one net.
+                                  ///<   more than one net.
     ERCE_BUS_ALIAS_CONFLICT,      ///< Conflicting bus alias definitions across sheets.
     ERCE_DRIVER_CONFLICT,         ///< Conflicting drivers (labels, etc) on a subgraph.
     ERCE_BUS_ENTRY_CONFLICT,      ///< A wire connected to a bus doesn't match the bus.
     ERCE_BUS_LABEL_ERROR,         ///< A label attached to a bus isn't in bus format.
     ERCE_BUS_TO_BUS_CONFLICT,     ///< A connection between bus objects doesn't share at least
-                                  ///< one net.
+                                  ///<   one net.
     ERCE_BUS_TO_NET_CONFLICT,     ///< A bus wire is graphically connected to a net port/pin
-                                  ///< (or vice versa).
+                                  ///<   (or vice versa).
     ERCE_NETCLASS_CONFLICT,       ///< Multiple labels assign different netclasses to same net.
     ERCE_GLOBLABEL,               ///< A global label is unique.
     ERCE_UNRESOLVED_VARIABLE,     ///< A text variable could not be resolved.
@@ -72,7 +73,7 @@ enum ERCE_T
     ERCE_SIMULATION_MODEL,        ///< An error was found in the simulation model.
     ERCE_WIRE_DANGLING,           ///< Some wires are not connected to anything else.
     ERCE_LIB_SYMBOL_ISSUES,       ///< Library symbol changed from current symbol in schematic or
-                                  ///< the library symbol link no longer valid.
+                                  ///<   the library symbol link no longer valid.
     ERCE_UNANNOTATED,             ///< Symbol has not been annotated.
     ERCE_EXTRA_UNITS,             ///< Symbol has more units than are defined.
     ERCE_DIFFERENT_UNIT_VALUE,    ///< Units of same symbol have different values.
diff --git a/gerbview/gerbview.cpp b/gerbview/gerbview.cpp
index d2160681db..84050ddf23 100644
--- a/gerbview/gerbview.cpp
+++ b/gerbview/gerbview.cpp
@@ -56,7 +56,7 @@ static struct IFACE : public KIFACE_BASE
             KIFACE_BASE( aName, aType )
     {}
 
-    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
+    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
 
     void OnKifaceEnd() override;
 
@@ -145,7 +145,7 @@ PGM_BASE* PgmOrNull()
 }
 
 
-bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
+bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
 {
     InitSettings( new GERBVIEW_SETTINGS );
     aProgram->GetSettingsManager().RegisterSettings( KifaceSettings() );
diff --git a/include/kiface_base.h b/include/kiface_base.h
index d7ea9b418d..fbe3f22516 100644
--- a/include/kiface_base.h
+++ b/include/kiface_base.h
@@ -41,7 +41,7 @@ public:
     /**
      * Typically #start_common() is called from here.
      */
-    virtual bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override = 0;
+    virtual bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override = 0;
 
     virtual void OnKifaceEnd() override
     {
diff --git a/include/kiface_ids.h b/include/kiface_ids.h
index ade5a70f28..ea2f2fbf14 100644
--- a/include/kiface_ids.h
+++ b/include/kiface_ids.h
@@ -50,12 +50,17 @@ enum KIFACE_ADDR_ID : int
      * Type is FP_LIB_TABLE*
      * Caller does NOT own.
      */
-     KIFACE_GLOBAL_FOOTPRINT_TABLE,
+    KIFACE_GLOBAL_FOOTPRINT_TABLE,
 
-     KIFACE_LOAD_SCHEMATIC,
-     KIFACE_NETLIST_SCHEMATIC,
-     KIFACE_SCRIPTING_LEGACY,
-     KIFACE_SCRIPTING
+    KIFACE_LOAD_SCHEMATIC,
+    KIFACE_NETLIST_SCHEMATIC,
+    KIFACE_SCRIPTING_LEGACY,
+    KIFACE_SCRIPTING,
+
+    KIFACE_TEST_FOOTPRINT_LINK,
+    KIFACE_TEST_FOOTPRINT_LINK_NO_LIBRARY,
+    KIFACE_TEST_FOOTPRINT_LINK_LIBRARY_NOT_ENABLED,
+    KIFACE_TEST_FOOTPRINT_LINK_NO_FOOTPRINT
 };
 
 #endif // KIFACE_IDS
diff --git a/include/kiway.h b/include/kiway.h
index 0263e66dfa..ab28a03a1e 100644
--- a/include/kiway.h
+++ b/include/kiway.h
@@ -174,7 +174,7 @@ struct KIFACE
      *         any UI because that is the duty of this function to say why it is returning
      *         false.  Never return false without having reported to the UI why.
      */
-    virtual bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) = 0;
+    virtual bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) = 0;
 
     /**
      * Called just once just before the DSO is to be unloaded.
diff --git a/pagelayout_editor/pl_editor.cpp b/pagelayout_editor/pl_editor.cpp
index 04c1fd1e88..b83cd47ff7 100644
--- a/pagelayout_editor/pl_editor.cpp
+++ b/pagelayout_editor/pl_editor.cpp
@@ -50,7 +50,7 @@ static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
             UNITS_PROVIDER( drawSheetIUScale, EDA_UNITS::MILLIMETRES )
     {}
 
-    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
+    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
 
     void OnKifaceEnd() override;
 
@@ -152,7 +152,7 @@ PGM_BASE* PgmOrNull()
     return process;
 }
 
-bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
+bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
 {
     InitSettings( new PL_EDITOR_SETTINGS );
     aProgram->GetSettingsManager().RegisterSettings( KifaceSettings() );
diff --git a/pcb_calculator/pcb_calculator.cpp b/pcb_calculator/pcb_calculator.cpp
index 962fdc3aef..6ce2e49f7d 100644
--- a/pcb_calculator/pcb_calculator.cpp
+++ b/pcb_calculator/pcb_calculator.cpp
@@ -39,7 +39,7 @@ static struct IFACE : public KIFACE_BASE
             KIFACE_BASE( aName, aType )
     {}
 
-    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
+    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
 
     void OnKifaceEnd() override;
 
@@ -100,7 +100,7 @@ PGM_BASE* PgmOrNull()
     return process;
 }
 
-bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
+bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
 {
     InitSettings( new PCB_CALCULATOR_SETTINGS );
     aProgram->GetSettingsManager().RegisterSettings( KifaceSettings() );
diff --git a/pcbnew/pcbnew.cpp b/pcbnew/pcbnew.cpp
index 211ff2c87f..ad23ebeafe 100644
--- a/pcbnew/pcbnew.cpp
+++ b/pcbnew/pcbnew.cpp
@@ -82,7 +82,7 @@ static struct IFACE : public KIFACE_BASE, public UNITS_PROVIDER
             UNITS_PROVIDER( pcbIUScale, EDA_UNITS::MILLIMETRES )
     {}
 
-    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
+    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
 
     void Reset() override;
 
@@ -393,7 +393,7 @@ FP_LIB_TABLE          GFootprintTable;
 FOOTPRINT_LIST_IMPL   GFootprintList;
 
 
-bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
+bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
 {
     // This is process-level-initialization, not project-level-initialization of the DSO.
     // Do nothing in here pertinent to a project!
@@ -416,7 +416,7 @@ bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
     if( !loadGlobalLibTable() )
         return false;
 
-    m_jobHandler = std::make_unique<PCBNEW_JOBS_HANDLER>();
+    m_jobHandler = std::make_unique<PCBNEW_JOBS_HANDLER>( aKiway );
 
     if( m_start_flags & KFCTL_CLI )
     {
diff --git a/pcbnew/pcbnew_jobs_handler.cpp b/pcbnew/pcbnew_jobs_handler.cpp
index 1544591122..533ca5c511 100644
--- a/pcbnew/pcbnew_jobs_handler.cpp
+++ b/pcbnew/pcbnew_jobs_handler.cpp
@@ -56,6 +56,9 @@
 #include <pcb_marker.h>
 #include <project/project_file.h>
 #include <exporters/export_svg.h>
+#include <kiface_ids.h>
+#include <netlist_reader/pcb_netlist.h>
+#include <netlist_reader/netlist_reader.h>
 #include <pcbnew_settings.h>
 #include <pcbplot.h>
 #include <pgm_base.h>
@@ -70,7 +73,8 @@
 #include "pcbnew_scripting_helpers.h"
 
 
-PCBNEW_JOBS_HANDLER::PCBNEW_JOBS_HANDLER()
+PCBNEW_JOBS_HANDLER::PCBNEW_JOBS_HANDLER( KIWAY* aKiway ) :
+        JOB_DISPATCHER( aKiway )
 {
     Register( "3d", std::bind( &PCBNEW_JOBS_HANDLER::JobExportStep, this, std::placeholders::_1 ) );
     Register( "svg", std::bind( &PCBNEW_JOBS_HANDLER::JobExportSvg, this, std::placeholders::_1 ) );
@@ -1002,6 +1006,46 @@ int PCBNEW_JOBS_HANDLER::JobExportDrc( JOB* aJob )
 
     m_reporter->Report( _( "Running DRC...\n" ), RPT_SEVERITY_INFO );
 
+    if( drcJob->m_parity )
+    {
+        typedef bool (*NETLIST_FN_PTR)( const wxString&, std::string& );
+
+        KIFACE*        eeschema = m_kiway->KiFACE( KIWAY::FACE_SCH );
+        wxFileName     schematicPath( drcJob->m_filename );
+        NETLIST_FN_PTR netlister = (NETLIST_FN_PTR) eeschema->IfaceOrAddress( KIFACE_NETLIST_SCHEMATIC );
+        std::string    netlist_str;
+        NETLIST        netlist;
+
+        schematicPath.SetExt( FILEEXT::KiCadSchematicFileExtension );
+
+        if( !schematicPath.Exists() )
+            schematicPath.SetExt( FILEEXT::LegacySchematicFileExtension );
+
+        if( !schematicPath.Exists() )
+        {
+            m_reporter->Report( _( "Failed to find schematic for parity tests.\n" ),
+                                RPT_SEVERITY_INFO );
+        }
+        else
+        {
+            (*netlister)( schematicPath.GetFullPath(), netlist_str );
+
+            try
+            {
+                auto lineReader = new STRING_LINE_READER( netlist_str, _( "Eeschema netlist" ) );
+                KICAD_NETLIST_READER netlistReader( lineReader, &netlist );
+                netlistReader.LoadNetlist();
+            }
+            catch( const IO_ERROR& e )
+            {
+                m_reporter->Report( _( "Failed to fetch schematic netlist for parity tests.\n" ),
+                                    RPT_SEVERITY_INFO );
+            }
+
+            drcEngine->SetSchematicNetlist( &netlist );
+        }
+    }
+
     drcEngine->SetProgressReporter( nullptr );
     drcEngine->SetViolationHandler(
             [&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer )
@@ -1017,7 +1061,8 @@ int PCBNEW_JOBS_HANDLER::JobExportDrc( JOB* aJob )
 
     commit.Push( _( "DRC" ), SKIP_UNDO | SKIP_SET_DIRTY );
 
-    // now "resolve" the drc exclusions again because its the only way to set exclusion status on a marker
+    // now "resolve" the drc exclusions again because its the only way to set exclusion status on
+    // a marker
     for( PCB_MARKER* marker : brd->ResolveDRCExclusions( false ) )
         brd->Add( marker );
 
@@ -1034,15 +1079,19 @@ int PCBNEW_JOBS_HANDLER::JobExportDrc( JOB* aJob )
     ratsnestProvider->SetSeverities( drcJob->m_severity );
     fpWarningsProvider->SetSeverities( drcJob->m_severity );
 
-    m_reporter->Report(
-            wxString::Format( _( "Found %d violations\n" ), markersProvider->GetCount() ),
-            RPT_SEVERITY_INFO );
-    m_reporter->Report(
-            wxString::Format( _( "Found %d unconnected items\n" ), ratsnestProvider->GetCount() ),
-            RPT_SEVERITY_INFO );
-    m_reporter->Report( wxString::Format( _( "Found %d schematic parity issues\n" ),
-                                          fpWarningsProvider->GetCount() ),
+    m_reporter->Report( wxString::Format( _( "Found %d violations\n" ),
+                                          markersProvider->GetCount() ),
                         RPT_SEVERITY_INFO );
+    m_reporter->Report( wxString::Format( _( "Found %d unconnected items\n" ),
+                                          ratsnestProvider->GetCount() ),
+                        RPT_SEVERITY_INFO );
+
+    if( drcJob->m_parity )
+    {
+        m_reporter->Report( wxString::Format( _( "Found %d schematic parity issues\n" ),
+                                              fpWarningsProvider->GetCount() ),
+                            RPT_SEVERITY_INFO );
+    }
 
     DRC_REPORT reportWriter( brd, units, markersProvider, ratsnestProvider, fpWarningsProvider );
 
diff --git a/pcbnew/pcbnew_jobs_handler.h b/pcbnew/pcbnew_jobs_handler.h
index 9c68cab9bd..33f7019099 100644
--- a/pcbnew/pcbnew_jobs_handler.h
+++ b/pcbnew/pcbnew_jobs_handler.h
@@ -24,6 +24,7 @@
 #include <jobs/job_dispatcher.h>
 #include <pcb_plot_params.h>
 
+class KIWAY;
 class BOARD;
 class DS_PROXY_VIEW_ITEM;
 class FOOTPRINT;
@@ -33,7 +34,7 @@ class JOB_FP_EXPORT_SVG;
 class PCBNEW_JOBS_HANDLER : public JOB_DISPATCHER
 {
 public:
-    PCBNEW_JOBS_HANDLER();
+    PCBNEW_JOBS_HANDLER( KIWAY* aKiway );
     int JobExportStep( JOB* aJob );
     int JobExportSvg( JOB* aJob );
     int JobExportDxf( JOB* aJob );
diff --git a/qa/mocks/include/mock_kiface_base.h b/qa/mocks/include/mock_kiface_base.h
index e894cf2edd..42da751b70 100644
--- a/qa/mocks/include/mock_kiface_base.h
+++ b/qa/mocks/include/mock_kiface_base.h
@@ -38,7 +38,7 @@ MOCK_BASE_CLASS( MOCK_KIFACE_BASE, KIFACE_BASE )
     MOCK_KIFACE_BASE() : KIFACE_BASE( "common_test", KIWAY::KIWAY_FACE_COUNT ) {};
     virtual ~MOCK_KIFACE_BASE() {};
 
-    MOCK_METHOD( OnKifaceStart, 2, bool( PGM_BASE*, int ) );
+    MOCK_METHOD( OnKifaceStart, 3, bool( PGM_BASE*, int, KIWAY* ) );
     MOCK_METHOD( OnKifaceEnd, 0, void() );
     MOCK_METHOD( CreateKiWindow, 4, wxWindow*( wxWindow*, int, KIWAY*, int ) );
     MOCK_METHOD( IfaceOrAddress, 1, void*( int ) );
diff --git a/qa/qa_utils/test_app_main.cpp b/qa/qa_utils/test_app_main.cpp
index df9b92cf6b..e5409fb59c 100644
--- a/qa/qa_utils/test_app_main.cpp
+++ b/qa/qa_utils/test_app_main.cpp
@@ -57,7 +57,7 @@ static struct IFACE : public KIFACE_BASE
             KIFACE_BASE( aName, aType )
     {}
 
-    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override
+    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override
     {
         return true;
     }
diff --git a/scripting/kicad_scripting_main.cpp b/scripting/kicad_scripting_main.cpp
index 8a989e92da..21051169d1 100644
--- a/scripting/kicad_scripting_main.cpp
+++ b/scripting/kicad_scripting_main.cpp
@@ -35,7 +35,7 @@ namespace KIPYTHON {
 
 static struct IFACE : public KIFACE_BASE
 {
-    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits ) override;
+    bool OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway ) override;
 
     void OnKifaceEnd() override;
 
@@ -109,7 +109,7 @@ PGM_BASE* PgmOrNull()
 }
 
 
-bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
+bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits, KIWAY* aKiway )
 {
     InitSettings( new KIPYTHON_SETTINGS );
     Pgm().GetSettingsManager().RegisterSettings( KifaceSettings() );