Skip to content
v0.1.3latest release121 skills4 categories6 sub-categories247 tested scripts397 reference docsContact us
scripts/eightd_check.py
#!/usr/bin/env python3
"""Check an 8D report discipline by discipline, as an automotive customer quality engineer would.

8D JSON keys (dates YYYY-MM-DD):
  id, opened, champion, team (list of roles),
  d2: {what, where, when, how_many, how_detected, is_not}
  d3: {actions, date, verified}
  d4: {occurrence_cause, occurrence_verified, escape_cause, escape_verified, method}
  d5: {actions: [{action, addresses: occurrence|escape}], verified}
  d6: {date, validation, containment_removed}
  d7: {fmea_updated, control_plan_updated, similar_products, lessons_learned}
  d8: {date, recognition}

Checks:
  8D01 medium  D1 team under 3 roles or no champion
  8D02 high    D2 problem description misses what / where / when / how many / how detected, or states a cause
  8D03 high    D3 containment missing, not verified, or later than --d3-hours after opening
  8D04 high    D4 missing or unverified occurrence or escape (non-detection) root cause, or 'human error'
  8D05 high    D5 no action for occurrence or for escape, or actions not verified before implementation
  8D06 medium  D6 no validation evidence, or interim containment not removed after validation
  8D07 medium  D7 FMEA / control plan not updated, or no horizontal deployment to similar products
  8D08 medium  discipline dates out of order, or D4 later than --d4-days after opening
  8D09 low     D8 closure / team recognition missing

Usage:
    python eightd_check.py 8d.json
    python eightd_check.py 8d.json --d3-hours 24 --d4-days 10

Exit codes: 0 no high findings, 1 high findings, 2 read or JSON error.
"""

import argparse
import datetime as dt
import json
import re
import sys

BLAME = re.compile(r"\b(operator|human|worker)\s+(error|mistake)|\bnot trained|\bcareless|\bforgot\b", re.I)
CAUSE_WORDS = re.compile(r"\b(because|due to|caused by|root cause)\b", re.I)


def date(s):
    try:
        return dt.date.fromisoformat(str(s)[:10])
    except (TypeError, ValueError):
        return None


def check(r, d3_hours, d4_days):
    f, status = [], {}
    sec = lambda k: r.get(k) or {}
    team = r.get("team") or []
    ok = len(team) >= 3 and r.get("champion")
    status["D1"] = ok
    if not ok:
        f.append(("8D01", "medium", "D1: name a champion and a cross-functional team of at least 3 roles"))
    d2 = sec("d2")
    miss = [k for k in ("what", "where", "when", "how_many", "how_detected") if not d2.get(k)]
    status["D2"] = not miss
    if miss:
        f.append(("8D02", "high", f"D2: problem description lacks {', '.join(miss)} (use 5W2H and is / is-not)"))
    if CAUSE_WORDS.search(str(d2.get("what", ""))):
        f.append(("8D02", "high", "D2: the problem statement contains a cause; describe the effect only"))
        status["D2"] = False
    if not d2.get("is_not"):
        f.append(("8D02", "high", "D2: add is / is-not (which parts, lots, lines, shifts are NOT affected)"))
        status["D2"] = False
    d3 = sec("d3")
    opened = date(r.get("opened"))
    d3d = date(d3.get("date"))
    status["D3"] = bool(d3.get("actions") and d3.get("verified"))
    if not d3.get("actions"):
        f.append(("8D03", "high", "D3: no containment actions"))
    elif not d3.get("verified"):
        f.append(("8D03", "high", "D3: containment not verified effective (e.g. sorted stock, clean-point date, certified parts)"))
    if opened and d3d and (d3d - opened).days * 24 > d3_hours:
        f.append(("8D03", "high", f"D3: containment {(d3d - opened).days} days after opening (expected within {d3_hours} h)"))
        status["D3"] = False
    d4 = sec("d4")
    status["D4"] = True
    for kind in ("occurrence", "escape"):
        cause = str(d4.get(f"{kind}_cause") or "")
        if not cause:
            f.append(("8D04", "high", f"D4: no {kind} root cause" + (" (why was it not detected?)" if kind == "escape" else "")))
            status["D4"] = False
        elif BLAME.search(cause):
            f.append(("8D04", "high", f"D4: {kind} cause '{cause[:50]}' is human error; why did the process allow it?"))
            status["D4"] = False
        elif not d4.get(f"{kind}_verified"):
            f.append(("8D04", "high", f"D4: {kind} cause not verified (reproduce it or switch it on/off)"))
            status["D4"] = False
    d5 = sec("d5")
    acts = d5.get("actions") or []
    status["D5"] = True
    for kind in ("occurrence", "escape"):
        if not any(str(x.get("addresses", "")).lower() == kind for x in acts):
            f.append(("8D05", "high", f"D5: no permanent corrective action for the {kind} cause"))
            status["D5"] = False
    if acts and not d5.get("verified"):
        f.append(("8D05", "high", "D5: verify the chosen actions work before implementing them (trial, capability run)"))
        status["D5"] = False
    d6 = sec("d6")
    status["D6"] = bool(d6.get("validation") and d6.get("containment_removed"))
    if not d6.get("validation"):
        f.append(("8D06", "medium", "D6: no validation evidence after implementation (data from production, Cpk, zero defects)"))
    elif not d6.get("containment_removed"):
        f.append(("8D06", "medium", "D6: record when D3 containment was removed after validation"))
    d7 = sec("d7")
    miss7 = [k for k in ("fmea_updated", "control_plan_updated", "similar_products") if not d7.get(k)]
    status["D7"] = not miss7
    if miss7:
        f.append(("8D07", "medium", f"D7: missing {', '.join(miss7)} (prevent recurrence across the system)"))
    dates = [("D3", d3d), ("D6", date(d6.get("date"))), ("D8", date(sec("d8").get("date")))]
    known = [(k, v) for k, v in dates if v]
    for (k1, v1), (k2, v2) in zip(known, known[1:]):
        if v2 < v1:
            f.append(("8D08", "medium", f"{k2} date {v2} is before {k1} date {v1}"))
    d4d = date(d4.get("date"))
    if opened and d4d and (d4d - opened).days > d4_days:
        f.append(("8D08", "medium", f"D4 completed {(d4d - opened).days} days after opening (target {d4_days})"))
    status["D8"] = bool(sec("d8").get("date"))
    if not status["D8"]:
        f.append(("8D09", "low", "D8: closure date and team recognition missing"))
    return f, status


def main():
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("report", help="8D JSON, or - for stdin")
    ap.add_argument("--d3-hours", type=int, default=24)
    ap.add_argument("--d4-days", type=int, default=10)
    a = ap.parse_args()
    try:
        r = json.load(sys.stdin if a.report == "-" else open(a.report, encoding="utf-8"))
        if not isinstance(r, dict):
            raise ValueError("8D JSON must be an object")
    except (OSError, ValueError) as e:
        print(f"error: {e}", file=sys.stderr)
        return 2
    f, status = check(r, a.d3_hours, a.d4_days)
    print(f"8D {r.get('id', '(no id)')}: " + "  ".join(f"{k} {'ok' if v else '--'}" for k, v in status.items()))
    for code, sev, msg in f:
        print(f"{code} [{sev}] {msg}")
    print(f"\n{len(f)} issue(s)" if f else "All disciplines complete.")
    return 1 if any(x[1] == "high" for x in f) else 0


if __name__ == "__main__":
    sys.exit(main())
More in Quality Management & ISO 9001

acceptance-sampling-plan-selector

Select and evaluate acceptance sampling plans for incoming, in-process and final inspection: ANSI/ASQ Z1.4 / ISO 2859-1 single sampling under normal inspection (code letter from lot size and inspection level I, II, III, S-1 to S-4; sample size, Ac and Re from AQL, following the table's arrows to the correct plan; 100% when n reaches the lot size; AQLs above 10 per 100 units), operating characteristic curves (Pa at any quality, producer's quality level, indifference point, LTPD, AOQ and AOQL, binomial or hypergeometric), plans designed from producer and consumer risk, and c = 0 zero-acceptance plans. Covers AQL choice by defect class, switching rules between normal, tightened and reduced inspection, when sampling is not appropriate, and random sampling.

approved-supplier-list-manager

Manage and audit an approved supplier list (ASL) / external provider register for ISO 9001 8.4.1, IATF 16949 8.4 and AS9100 8.4.1: supplier status (approved, conditional, probation, pending, disqualified), scope of approval by commodity and process, risk class, certification and expiry, re-evaluation intervals by risk, conditions with end dates, single-source contingency, approver and customer-directed sources. Audits the list for expired or expiring certificates, overdue re-evaluations, conditional or probation status past its decision date, high-risk suppliers without the required certification, single-source suppliers without backup and missing approvers, and checks purchase orders for suppliers that are off-list, disqualified or pending, or buying outside the approved scope. Covers ERP blocks, PO requirements, off-list and emergency purchases.

apqp-phase-planner

Plan and track Advanced Product Quality Planning (AIAG APQP 3rd edition, IATF 16949) for a new or changed automotive part: back-schedule the five phases (plan and define, product design, process design, product and process validation, launch and feedback) from the start-of-production date by program complexity, with overlap, PPAP lead and safe-launch period; list the deliverables and gate for each phase (DFMEA, DVP&R, feasibility, process flow, PFMEA, control plans, MSA, Ppk, run at rate, PPAP, safe launch, lessons learned); adjust for build-to-print suppliers; flag compressed timelines and late or untracked deliverables against a status list. Covers gate reviews, red/yellow/green status reporting, run at rate and safe-launch exit criteria.

as9100-gap-analyzer

Run an AS9100D / EN 9100:2018 gap analysis for aviation, space and defence suppliers on the requirements added to ISO 9001: on-time delivery and conformity focus, management representative, ethics and human-factors awareness, operational planning and operational risk management, configuration management, product safety, counterfeit-part prevention, special requirements, critical items and key characteristics, external provider register and flow-down, FOD prevention, tooling and NC program control, special process validation, first article inspection (AS9102), traceability, in-service data and nonconforming product control. Generates and scores a worksheet, grades missing core controls as major, and checks invalid n/a claims. Covers AS9101/AS9104, OASIS, AS9102 FAI forms, counterfeit standards (AS5553, AS6174, AS6081), NADCAP and the planned IA9100 transition.

audit-report-writer

Write internal, supplier and pre-certification audit reports with the content ISO 19011:2018 6.5 expects: audit objectives, scope, criteria with revisions, dates, team and auditees, method and sampling statement, specific strengths, findings table and three-part finding details (requirement, evidence, statement), conclusion on the degree of fulfilment of criteria, diverging opinions, follow-up deadlines and distribution. Builds a Markdown report from a JSON audit record, generates the conclusion from the findings (major / minor / none) and flags reports missing required content or findings without clause or evidence. Covers conclusion wording, closing meetings, response expectations and verifying corrective actions.

capa-effectiveness-checker

Verify whether a corrective action actually worked (ISO 9001 10.2.1 d, IATF 16949, 8D D6) with statistics instead of 'no complaints so far': before/after defect-rate comparison with an exact conditional binomial test for Poisson counts and a minimum practical reduction, zero-defect demonstration size (n = ln(1-C)/ln(1-p), rule of three) with progress so far, and exact Clopper-Pearson upper bounds on the post-action defect rate. Returns effective, not yet demonstrated (with the units still needed) or not effective. Covers setting effectiveness criteria when the CAPA is opened, choosing the window by problem frequency, leading indicators for rare failures (Cpk, SPC stability, error-proofing checks, LPA), and traps such as volume changes, detection changes and containment masking.