CMDB Health Dashboard — Compliance Score KPI: What Runs, How It Works, How CIs Are Evaluated, What's Stored, and Where to VerifySummary<!-- /*NS Branding Styles*/ --> .ns-kb-css-body-editor-container { p { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } span { font-size: 12pt; font-family: Lato; color: var(--now-color--text-primary, #000000); } h2 { font-size: 24pt; font-family: Lato; color: var(--now-color--text-primary, black); } h3 { font-size: 18pt; font-family: Lato; color: var(--now-color--text-primary, black); } h4 { font-size: 14pt; font-family: Lato; color: var(--now-color--text-primary, black); } a { font-size: 12pt; font-family: Lato; color: var(--now-color--link-primary, #00718F); } a:hover { font-size: 12pt; color: var(--now-color--link-primary, #024F69); } a:target { font-size: 12pt; color: var(--now-color--link-primary, #032D42); } a:visited { font-size: 12pt; color: var(--now-color--link-primary, #00718f); } ul { font-size: 12pt; font-family: Lato; } li { font-size: 12pt; font-family: Lato; } img { display: ; max-width: ; width: ; height: ; } } Overview Ever looked at the Compliance KPI on the CMDB Health Dashboard and wondered why it looks empty, stuck at 0, or behaves nothing like Completeness or Correctness? You are not alone — and there is a very specific reason for it. Compliance works completely differently from every other KPI on the dashboard. It does not scan your CIs. It does not evaluate field values. It does not check relationships. Instead, it depends entirely on Data Certification Audits that you configure and run separately. The health job simply reads whatever those audits already produced. If your audits are not set up correctly or have not run, the Compliance score will be 0 or missing — and the health job is not the problem. This article walks you through exactly how it works, what the engine is looking for, and where things go wrong. How Compliance is different from other KPIs — this is the most important thing to understand A lot of confusion comes from assuming Compliance works the same way as Completeness or Correctness. It does not. KPIDoes the health job scan CIs directly?What drives the score?Completeness YesField population rules evaluated per CICorrectness YesStaleness, Orphan, and Duplicate rules evaluated per CICompliance NoResults from Data Certification Audits you configure separately → For Completeness and Correctness, the health job is the evaluator. It looks at your CIs and decides what passes and what fails. → For Compliance, the health job is only a reader. It looks at cert_audit_result rows that your Data Certification Audits already produced and counts the failures from there. → This means Compliance has a two-step dependency that no other KPI has: StepWhat needs to happenStep 1Your Data Certification Audit must be configured, active, and must have actually run — producing cert_audit_result rows with state = FailedStep 2The CMDB Health job runs, reads those rows, writes failures, and computes the score → If Step 1 has not happened correctly, Step 2 produces nothing — silently. No error, no alert, just a missing or zero score. What Compliance actually includes → Compliance is a parent KPI with only one child sub-metric: AUDIT. → AUDIT evaluates CIs against your active Data Certification Audits. A CI is a Compliance failure if it has a Failed row in cert_audit_result within the last audit run window. → Because AUDIT is the only child, the engine never writes a separate Compliance scorecard row. The dashboard KPI widget reads AUDIT's rows from cmdb_health_scorecard. When investigating, always filter by AUDIT's sys_id — not COMPLIANCE's sys_id. When and how the evaluation runs → The scheduled job fires and instantiates a fresh ComplianceManager every run. It verifies the job is running from the global domain — if triggered from a sub-domain it exits immediately. → A mutex prevents two runs from executing simultaneously. If a previous run appears stuck in IN_PROGRESS with no activity for more than glide.cmdb.health.max.inactivity.period days (default 1 day), the hung run is cleaned up and a fresh one starts. → The engine finds AUDIT as the only active child of Compliance and starts the audit batch setup. Which audits get picked up → The engine queries cert_audit at the start of every run. Two types of audits qualify: Audit typeConditions to be picked upTemplate-basedActive, has a template assigned, targets cmdb_ci or a subclass tableScriptedActive, type = scripted, no template, targets cmdb_ci or a subclass table → Audits targeting non-CMDB tables are never picked up — even if active and fully configured. The query only looks at cmdb_ci subclass tables. → Each qualifying audit becomes one processing batch, all loaded under the global domain. The most common reasons Compliance score is 0 or a class is missing This is where most customer confusion happens. Here are the exact reasons from the code, in order of how often they occur: → Audit has never run — last_run_date on cert_audit is empty. The processor silently skips the audit and marks it as complete with a skipped flag. No error is surfaced. No failure is written. The audit simply does not contribute to the score. → Scripted audit not calling updateLastRunDate — for scripted audits, last_run_date is never updated automatically. The script must explicitly call new SNC.CertificationProcessing().updateLastRunDate(auditId) at the very start of the script before any results are persisted. If this call is missing, last_run_date stays empty forever and the health job skips the audit on every single health run — no matter how many times the audit itself has executed. → Audit running when the health job fires — the engine falls back to the previous completed run window using prev_last_run_date. If the audit has only ever completed one run, prev_last_run_date is also empty and the audit is skipped again. → Audit targets the wrong table — if cert_audit.table is not cmdb_ci or one of its subclasses, the health job never sees it. → Audit ran but produced no Failed results — last_run_date is populated but cert_audit_result has no Failed rows in the window. Either all CIs passed or the audit conditions did not match anything. → Audit is inactive — excluded from the batch query entirely. How the audit result time window works → For each audit batch, the processor determines what time window to pull cert_audit_result rows from: ScenarioWindow usedNormal — audit completed its last runlast_run_date to nowAudit currently running or in error stateprev_last_run_date to last_run_dateAudit never ran, or prev run unknownAudit is skipped entirely Exactly when a CI is marked a Compliance failure → The processor queries cert_audit_result for rows where state = Failed and sys_created_on falls within the determined window for that audit. → Results are fetched in batches ordered by CI sys_id. The default batch size is 10,000 rows per pass, with a maximum of 100,000. The effective size is also capped by the failure_threshold configured on the metric. → A CI can have multiple Failed rows in cert_audit_result for the same audit run — one per failing field or condition. All of these are appended into the description of a single cmdb_health_result row for that CI, capturing the audit name, date, field name, desired value, and actual discrepancy value. How the total is calculated — another key difference from other KPIs → This surprises most customers. The total used in the Compliance score is not a count of all CIs in the class from cmdb_ci. → The total is the count of CIs that the audit actually evaluated in its last run window — meaning CIs that appear in cert_audit_result for that window, whether they passed or failed. → If a CI was not evaluated by any active audit, it is not in the total. It does not count as passing. It does not count as failing. It is completely invisible to the score. → CIs deleted after the audit ran (null sys_id or class name in the result) are also excluded from the total. How the score is calculated → Score calculation fires after AUDIT reaches a completed or max-failures state. → Before scoring starts, the engine checks whether the read replica has caught up. If not, scoring is delayed and retried after glide.cmdb.health.metricProcessor.scoreCalculationDelay minutes (default 15 minutes). → For each CI class the score is: failed divided by total, multiplied by 100, rounded to the nearest integer. ValueSourcetotalCIs from cert_audit_result last window for that class and all its subclassesfailedDistinct count of CI sys_ids from cmdb_health_result where to_delete = false, for that class and all subclassesscoreMath.round((failed * 100) / total) → The COMPLIANCE parent scorecard row is never written. The dashboard reads cmdb_health_scorecard where metric = AUDIT's sys_id and status = COMPLETE. → If total = 0 for a class, no scorecard row is written and that class will not appear on the dashboard at all. → The score is a failure rate, not a health percentage. Score = 20 means 20% of audited CIs failed. Score = 0 means no failures were detected. How scores are activated and results cleaned up → After scoring completes, the engine runs cleanup in this order: → Failing CIs (to_delete = false) are set active = true in cmdb_health_result. Resolved CIs (to_delete = true) are set active = false. → All IN_PROGRESS scorecard rows are flipped to COMPLETE. The previous COMPLETE rows become HISTORIC. After this, the new scores are visible on the dashboard. → The parent Compliance metric status is set to COMPLETE if all children finished cleanly, or INCOMPLETE if any child hit the failure threshold before scanning all CIs. → All cmdb_health_result rows still marked to_delete = true are physically deleted. Rows with a linked task are deleted via GlideRecord so the associated Business Rule can close the task. Where results and scores are stored → cmdb_health_result — one row per failing CI. Filter by metric = AUDIT's sys_id. Key fields: ci, class_name, metric, source, description, task, active, to_delete, last_evaluated_on, discovery_source, ownership. → cmdb_health_result_count — aggregated failure counts per metric per domain used to avoid repeated count queries during the run. → cmdb_health_scorecard — one row per class per domain. Filter by metric = AUDIT's sys_id and status = COMPLETE. Key fields: class, metric, total, failed, score, status, evaluated_on. → cmdb_health_scorecard_service / cmdb_health_scorecard_group — same structure keyed by service or CMDB group if those score passes are enabled. Tables you will use to verify every stage → cert_audit — is the audit active? Does it have last_run_date populated? Does it target a cmdb_ci subclass table? For scripted audits, does the script call updateLastRunDate at the start? → cert_audit_result — are there rows with state = Failed for your audit sys_id within the expected window? This is the raw source of all Compliance failures. If this table has no Failed rows, the health job has nothing to process. → cmdb_health_processor_status — was the audit batch processed or skipped? A skipped = true flag means the audit was skipped due to missing last_run_date or unresolvable run state. → cmdb_health_result — filter by metric = AUDIT's sys_id. Check active, to_delete, source, last_evaluated_on, and description per failing CI. → cmdb_health_result_count — aggregated failure counts per domain during and after a run. → cmdb_health_scorecard — filter by metric = AUDIT's sys_id and status = COMPLETE. Confirm total, failed, and score per class. A missing class means total was 0 — the audit evaluated no CIs of that class in its last window. → cmdb_health_metric_status — is the run stuck in IN_PROGRESS or did it end as INCOMPLETE? Typical verification path → Start with cert_audit — active? last_run_date populated? Audit ever ran? → Check cert_audit_result — Failed rows within the last run window? → Check cmdb_health_processor_status — processed or skipped? → Check cmdb_health_result — active = true, to_delete = false rows for AUDIT's sys_id? → Check cmdb_health_scorecard — COMPLETE rows for AUDIT's sys_id with updated total, failed, and score? Takeaways → Compliance is driven entirely by Data Certification Audits. The health job reads audit results — it does not produce them. If your audits are not running and producing Failed results in cert_audit_result, the Compliance score will always be empty. → The Compliance KPI score is the AUDIT sub-metric's score. The COMPLIANCE parent never gets its own scorecard row. Always query cmdb_health_result and cmdb_health_scorecard using AUDIT's sys_id. → If last_run_date is empty on cert_audit, the audit is silently skipped every health run. For scripted audits this will always be the case unless updateLastRunDate is called at the start of the script. → The total in the score formula is not all CIs in the class — it is only CIs the audit actually evaluated in its last run window. CIs outside any audit's scope are completely invisible to Compliance scoring. → Results in cmdb_health_result and scores in cmdb_health_scorecard both use AUDIT's sys_id as the metric reference — not COMPLIANCE's. → Score = failure rate. Score 0 = no failures found. A class missing from the dashboard means total was 0 for it — the audit evaluated no CIs of that class in its last run.