Build a CAPA Process Intelligence Case

Mine a synthetic CAPA event log to expose rework, control gaps, and priorities.

Introduction

30 Second Summary

A quality dashboard can show work as on target while its average hides repeated fixes. Skipped checks can stay invisible too.

In this project, you will investigate a synthetic CAPA process to expose why an on-target average can hide rework alongside skipped controls. Your finished portfolio case combines a responsible AI brief, a current Celonis capability translation, and a public GitHub repository for client or employer review.

What You'll Build

Your public repository opens with an ON TARGET average, then guides reviewers through five process variants and the 14 cases carrying at least one deviation.

By the end of this project, you'll have:

  • A synthetic event log you can open in Microsoft Excel to trace any CAPA case from Nonconformance Logged to CAPA Closed.
  • A directly-follows process map that makes the dominant path visible beside its containment bypass and rework loop.
  • A public GitHub portfolio repository where reviewers can inspect the evidence-governed brief, process files, synthetic-data statement, and Celonis capability translation from one link.
  • Secret Mission: Build a transparent risk score that sorts all 30 CAPA cases into a prioritized intervention queue.

Are there any prerequisites?

You need a 64-bit Windows 10 or newer device with permission to install local applications. You also need internet access, Microsoft Excel, a personal Microsoft account, Celonis Academy training access, a GitHub account, and GitHub Desktop.

Before We Start

This first checkpoint commits you to the investigation before any hands-on work begins. Your synthetic-data-only goal is to determine why an apparently on-target CAPA process still contains rework and control failures.

Set Up the Process-Mining Workspace

Your CAPA investigation needs a reproducible local workspace that can generate synthetic data without relying on a business Microsoft tenant or a general-purpose Celonis sandbox.

You will install Python and Graphviz. You will then create an isolated environment for PM4Py before generating the first portfolio artifact.

In this step, get ready to:
  • Install Python 3.14 through the Python launcher.
  • Install Graphviz 16.1.0 for process-map rendering.
  • Create the project workspace and generate a synthetic CAPA event log.
Install Python and Graphviz

Python runs the analysis script. The Python launcher lets Windows select the required 3.14 runtime even when another Python version is also installed.

  • Press the Windows key to open Windows search.
  • Type PowerShell.
  • Press Enter to open PowerShell.
  • Check for the Python 3.14 runtime by running this command:
py -3.14 --version

✔️ I see Python 3.14

Your Python launcher can find the required runtime. Keep this PowerShell window open for the next check.

ⓧ I see an older version

An older runtime can remain installed. The Python launcher supports selecting Python 3.14 separately.

  • Install the Python 3.14 runtime by running this command:
py install 3.14

The download can take a few minutes. You will see the installer finish before PowerShell returns to the prompt.

  • Confirm the new runtime by running the version check again:
py -3.14 --version

You should now see a Python 3.14 version.

Still seeing an older version?

Close PowerShell after the installation completes. Open a new PowerShell window so the launcher can refresh its available runtimes.

Ask for help if the launcher still selects the wrong runtime: Help me verify Python 3.14 through the Windows Python launcher.

ⓧ The command is not found

The Python install manager provides the launcher used throughout this project.

  • Use the official Python on Windows guide to install the Python install manager from the Microsoft Store or Python.org.
  • Close PowerShell after the installation completes.
  • Open a new PowerShell window through Windows search.
  • Install Python 3.14 by running this command:
py install 3.14
  • Confirm the runtime by running this version check:
py -3.14 --version

You should now see a Python 3.14 version.

Launcher still unavailable?

Make sure you opened a new PowerShell window after installing the manager. An existing window may not recognize the newly installed command.

Ask for help with your installation state: Help me make the Python launcher available in PowerShell.

Graphviz converts the process relationships discovered later into an SVG map. Its renderer must be available through the dot command.

  • Check for Graphviz by running this command:
dot -V

✔️ I see version 16.1.0 or higher

Graphviz is installed. The version output proves that its renderer is available from PowerShell.

ⓧ I see an older version

Upgrade Graphviz so this project uses the current Windows package.

  • Upgrade the installed package by running this command:
winget upgrade graphviz
  • Close PowerShell after the upgrade completes.
  • Open a new PowerShell window through Windows search.
  • Confirm the upgraded renderer by running:
dot -V

You should now see Graphviz version 16.1.0 or higher.

Upgrade not detected?

A new PowerShell window is required when an installer changes the system path. Use the official Graphviz download page if Windows Package Manager cannot find an upgrade.

Ask for help with the installed package: Help me update Graphviz on Windows.

ⓧ The command is not found

Windows cannot currently find the Graphviz renderer. Installing its Windows package adds the renderer to your device.

  • Install Graphviz by running this command:
winget install graphviz

The installer may take a few minutes. PowerShell returns to the prompt when the package installation finishes.

  • Close PowerShell after the installation completes.
  • Open a new PowerShell window through Windows search.
  • Confirm the renderer by running:
dot -V

You should see the installed Graphviz version.

Still cannot find dot?

Graphviz rendering depends on its installation directory being available on the system path. Confirm that you opened a new PowerShell window after installation.

Ask for help if the renderer remains unavailable: Help me make the Graphviz dot command available.

Create the project files

The project folder keeps the script, dependency pin, responsible-AI prompt, virtual environment, and generated files together. You will create it directly under C:\ so every later command uses the same path.

  • Create the C:\capa-process-intelligence folder by running this command in PowerShell:
New-Item -Path "C:\" -Name "capa-process-intelligence" -ItemType "Directory"

PowerShell lists the new capa-process-intelligence directory. That visible entry confirms the workspace exists.

  • Move into the project folder by running this command:
cd C:\capa-process-intelligence

The dependency file pins the exact PM4Py release and its stable dependency set. This keeps the analysis reproducible across future runs.

  • Create requirements.txt inside C:\capa-process-intelligence with this exact content:
pm4py[stable]==2.7.23.11

What does this requirement do?

The version pin installs PM4Py 2.7.23.11. The stable extra installs the dependency versions validated by the PM4Py project.

  • Save requirements.txt.
  • Create copilot_prompt.txt inside C:\capa-process-intelligence with this exact content:
You are supporting a process intelligence review of a synthetic CAPA event log.

Use only the evidence pasted below. Do not invent causes, savings, regulations, benchmarks, or source-system details.

For every material statement, apply exactly one label:
- EVIDENCE: directly supported by a metric in the evidence pack
- HYPOTHESIS: a possible explanation that requires validation
- DECISION: a proposed action that requires an accountable human owner

Return:
1. A five-sentence executive summary
2. A table of the three most important findings
3. Up to three root-cause hypotheses and the data needed to test each one
4. A 30-day action plan with an owner role and validation measure for each action
5. A responsible-AI control note explaining why the evidence must be checked by a human

Evidence pack:
[PASTE THE CONTENTS OF outputs\evidence_pack.txt HERE]

What does this prompt control?

The prompt limits the future AI review to supplied synthetic evidence. Its labels separate measured facts from possible explanations and human-owned actions.

  • Save copilot_prompt.txt.
  • Confirm the first two files exist by running:
ls

You should see requirements.txt and copilot_prompt.txt in the folder listing.

  • Create capa_process_intelligence.py inside C:\capa-process-intelligence by copying the Python script from the full-code tab below.
  • Save capa_process_intelligence.py.

✔️ Awesome, I've got everything!

Great. Confirm that requirements.txt, copilot_prompt.txt, and capa_process_intelligence.py are saved inside C:\capa-process-intelligence.

ⓧ I'd like to double check the full code

Compare your complete capa_process_intelligence.py file with this reference:

import argparse
import csv
from collections import Counter
from datetime import datetime, timedelta
from pathlib import Path
from statistics import mean, median

import pandas as pd
import pm4py

BASE_DIR = Path(__file__).resolve().parent
EVENT_LOG_PATH = BASE_DIR / "capa_event_log.csv"
OUTPUT_DIR = BASE_DIR / "outputs"
SYNTHETIC_TARGET_DAYS = 20
TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"

RESOURCE_BY_ACTIVITY = {
    "Nonconformance Logged": "Quality Technician",
    "Containment Applied": "Operations Supervisor",
    "Root Cause Analyzed": "Quality Engineer",
    "Corrective Action Implemented": "CAPA Owner",
    "Effectiveness Verified": "Quality Manager",
    "CAPA Closed": "Quality Manager",
}

PROFILES = [
    (
        "happy_path",
        [
            "Nonconformance Logged",
            "Containment Applied",
            "Root Cause Analyzed",
            "Corrective Action Implemented",
            "Effectiveness Verified",
            "CAPA Closed",
        ],
        [0, 1, 4, 7, 10, 12],
        "Low",
        800,
        16,
    ),
    (
        "rework_loop",
        [
            "Nonconformance Logged",
            "Containment Applied",
            "Root Cause Analyzed",
            "Corrective Action Implemented",
            "Effectiveness Verified",
            "Root Cause Analyzed",
            "Corrective Action Implemented",
            "Effectiveness Verified",
            "CAPA Closed",
        ],
        [0, 1, 4, 7, 10, 13, 18, 23, 28],
        "High",
        6500,
        5,
    ),
    (
        "skipped_containment",
        [
            "Nonconformance Logged",
            "Root Cause Analyzed",
            "Corrective Action Implemented",
            "Effectiveness Verified",
            "CAPA Closed",
        ],
        [0, 3, 6, 9, 12],
        "Medium",
        3200,
        4,
    ),
    (
        "late_closure",
        [
            "Nonconformance Logged",
            "Containment Applied",
            "Root Cause Analyzed",
            "Corrective Action Implemented",
            "Effectiveness Verified",
            "CAPA Closed",
        ],
        [0, 2, 8, 18, 28, 35],
        "High",
        7200,
        3,
    ),
    (
        "missing_effectiveness",
        [
            "Nonconformance Logged",
            "Containment Applied",
            "Root Cause Analyzed",
            "Corrective Action Implemented",
            "CAPA Closed",
        ],
        [0, 1, 5, 12, 18],
        "High",
        4800,
        2,
    ),
]


def write_dict_rows(path, rows, fieldnames):
    with path.open("w", newline="", encoding="utf-8") as handle:
        writer = csv.DictWriter(handle, fieldnames=fieldnames)
        writer.writeheader()
        for row in rows:
            writer.writerow(row)


def generate_event_log():
    rows = []
    base_start = datetime(2026, 1, 5, 8, 0, 0)
    case_number = 1

    for profile, activities, offsets, severity, base_cost, case_count in PROFILES:
        for profile_index in range(case_count):
            case_id = f"CAPA-{case_number:03d}"
            site = "Plant North" if case_number % 2 else "Plant South"
            case_start = base_start + timedelta(days=(case_number - 1) * 2)
            increment = 50 if profile == "happy_path" else 250
            cost_impact = base_cost + profile_index * increment

            for activity, day_offset in zip(activities, offsets):
                event_time = case_start + timedelta(days=day_offset)
                timestamp = event_time.strftime(TIMESTAMP_FORMAT)
                rows.append(
                    {
                        "case_id": case_id,
                        "activity": activity,
                        "timestamp": timestamp,
                        "start_timestamp": timestamp,
                        "site": site,
                        "severity": severity,
                        "resource": RESOURCE_BY_ACTIVITY[activity],
                        "cost_impact": cost_impact,
                    }
                )
            case_number += 1

    fieldnames = [
        "case_id",
        "activity",
        "timestamp",
        "start_timestamp",
        "site",
        "severity",
        "resource",
        "cost_impact",
    ]
    write_dict_rows(EVENT_LOG_PATH, rows, fieldnames)
    print(
        f"Created synthetic event log: {EVENT_LOG_PATH.name} "
        f"({len(rows)} events across {case_number - 1} cases)"
    )


def ensure_event_log():
    if not EVENT_LOG_PATH.exists():
        generate_event_log()


def read_event_log():
    ensure_event_log()
    with EVENT_LOG_PATH.open(newline="", encoding="utf-8") as handle:
        return list(csv.DictReader(handle))


def group_cases(rows):
    cases = {}
    for row in rows:
        cases.setdefault(row["case_id"], []).append(row)
    for events in cases.values():
        events.sort(key=lambda row: row["timestamp"])
    return cases


def get_duration_records(cases):
    records = []
    for case_id, events in sorted(cases.items()):
        first_time = datetime.strptime(events[0]["timestamp"], TIMESTAMP_FORMAT)
        last_time = datetime.strptime(events[-1]["timestamp"], TIMESTAMP_FORMAT)
        closure_days = (last_time - first_time).total_seconds() / 86400
        records.append({"case_id": case_id, "closure_days": closure_days})
    return records


def headline_metrics():
    rows = read_event_log()
    cases = group_cases(rows)
    duration_records = get_duration_records(cases)
    durations = [record["closure_days"] for record in duration_records]
    average_days = mean(durations)
    median_days = median(durations)
    status = "ON TARGET" if average_days <= SYNTHETIC_TARGET_DAYS else "OFF TARGET"

    metrics = {
        "case_count": len(cases),
        "average_days": average_days,
        "median_days": median_days,
        "status": status,
    }

    print(f"Cases: {metrics['case_count']}")
    print(f"Average closure days: {metrics['average_days']:.1f}")
    print(f"Median closure days: {metrics['median_days']:.1f}")
    print(f"Synthetic target: <= {SYNTHETIC_TARGET_DAYS} days")
    print(f"Headline status: {metrics['status']}")
    return metrics


def discover_process():
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    dataframe = pd.read_csv(EVENT_LOG_PATH)
    dataframe = pm4py.format_dataframe(
        dataframe,
        case_id="case_id",
        activity_key="activity",
        timestamp_key="timestamp",
        start_timestamp_key="start_timestamp",
        timest_format=TIMESTAMP_FORMAT,
    )
    dfg, start_activities, end_activities = pm4py.discover_dfg(
        dataframe,
        case_id_key="case_id",
        activity_key="activity",
        timestamp_key="timestamp",
    )

    process_map_path = OUTPUT_DIR / "process_map.svg"
    pm4py.save_vis_dfg(
        dfg,
        start_activities,
        end_activities,
        str(process_map_path),
        rankdir="LR",
        graph_title="CAPA Directly-Follows Graph",
    )

    edge_rows = []
    for (source, target), frequency in sorted(
        dfg.items(), key=lambda item: (item[0][0], item[0][1])
    ):
        edge_rows.append(
            {
                "source_activity": source,
                "target_activity": target,
                "frequency": frequency,
            }
        )
    write_dict_rows(
        OUTPUT_DIR / "directly_follows_edges.csv",
        edge_rows,
        ["source_activity", "target_activity", "frequency"],
    )

    rows = read_event_log()
    cases = group_cases(rows)
    variant_counter = Counter(
        " -> ".join(event["activity"] for event in events)
        for events in cases.values()
    )
    variant_rows = []
    for rank, (variant, count) in enumerate(variant_counter.most_common(), start=1):
        variant_rows.append(
            {
                "variant_rank": rank,
                "variant": variant,
                "case_count": count,
                "case_percentage": round(count / len(cases) * 100, 1),
            }
        )
    write_dict_rows(
        OUTPUT_DIR / "variant_summary.csv",
        variant_rows,
        ["variant_rank", "variant", "case_count", "case_percentage"],
    )

    print(f"Created {process_map_path}")
    print(f"Created {OUTPUT_DIR / 'variant_summary.csv'} ({len(variant_rows)} variants)")
    print(f"Created {OUTPUT_DIR / 'directly_follows_edges.csv'}")
    return variant_rows


def analyze_cases():
    rows = read_event_log()
    cases = group_cases(rows)
    duration_map = {
        record["case_id"]: record["closure_days"]
        for record in get_duration_records(cases)
    }
    findings = []

    for case_id, events in sorted(cases.items()):
        activities = [event["activity"] for event in events]
        closure_days = duration_map[case_id]
        rework = (
            activities.count("Root Cause Analyzed") > 1
            or activities.count("Corrective Action Implemented") > 1
        )
        missing_containment = "Containment Applied" not in activities
        missing_effectiveness = "Effectiveness Verified" not in activities
        late = closure_days > SYNTHETIC_TARGET_DAYS

        issues = []
        if rework:
            issues.append("Rework loop")
        if missing_containment:
            issues.append("Missing containment")
        if missing_effectiveness:
            issues.append("Missing effectiveness verification")
        if late:
            issues.append("Closure over synthetic target")

        findings.append(
            {
                "case_id": case_id,
                "site": events[0]["site"],
                "severity": events[0]["severity"],
                "cost_impact": int(events[0]["cost_impact"]),
                "closure_days": round(closure_days, 1),
                "rework": rework,
                "missing_containment": missing_containment,
                "missing_effectiveness": missing_effectiveness,
                "late_over_20_days": late,
                "status": "Investigate" if issues else "Compliant",
                "issues": "; ".join(issues) if issues else "None",
            }
        )

    write_dict_rows(
        OUTPUT_DIR / "case_findings.csv",
        findings,
        [
            "case_id",
            "site",
            "severity",
            "cost_impact",
            "closure_days",
            "rework",
            "missing_containment",
            "missing_effectiveness",
            "late_over_20_days",
            "status",
            "issues",
        ],
    )
    return findings


def write_evidence_pack(metrics, variant_rows, findings):
    deviation_count = sum(row["status"] == "Investigate" for row in findings)
    late_count = sum(row["late_over_20_days"] for row in findings)
    rework_count = sum(row["rework"] for row in findings)
    containment_count = sum(row["missing_containment"] for row in findings)
    effectiveness_count = sum(row["missing_effectiveness"] for row in findings)

    evidence = f"""CAPA Process Intelligence Evidence Pack

Data status: Synthetic portfolio data only
Cases: {metrics['case_count']}
Average closure days: {metrics['average_days']:.1f}
Median closure days: {metrics['median_days']:.1f}
Synthetic target: <= {SYNTHETIC_TARGET_DAYS} days
Headline status: {metrics['status']}
Distinct process variants: {len(variant_rows)}
Dominant variant cases: {variant_rows[0]['case_count']}
Cases with at least one deviation: {deviation_count}
Cases over the synthetic target: {late_count}
Cases with a rework loop: {rework_count}
Cases missing containment: {containment_count}
Cases missing effectiveness verification: {effectiveness_count}

Interpretation boundary:
- Measured values above are EVIDENCE from the generated files.
- Possible causes are HYPOTHESES until validated with process owners and source-system data.
- Improvement priorities are DECISIONS that require an accountable human owner.
"""
    (OUTPUT_DIR / "evidence_pack.txt").write_text(evidence, encoding="utf-8")


def write_celonis_translation():
    translation = """# Celonis Capability Translation

This project uses PM4Py and synthetic data. It does not claim to reproduce the Celonis platform.

| Portfolio output | Closest current Celonis concept | Interview explanation |
| --- | --- | --- |
| `capa_event_log.csv` | Event log | The case ID, activity, and timestamp reconstruct each case path. |
| `process_map.svg` | Process Explorer | The directly-follows map helps expose undesired events and connections. |
| `variant_summary.csv` | Variant Explorer | The variant table summarizes the different paths cases follow. |
| `case_findings.csv` | Process Adherence Manager | Explicit rules compare observed behavior with the intended control path. |
| AI-assisted brief | Celonis Insight Explorer | Both support insight generation, but free Copilot does not reproduce Celonis data integration or KPI pattern detection. |

Academy follow-up:
- Locate the `Use and Interpret Views` training track.
- Compare this project with `Use the Variant Explorer in Views`.
- Compare this project with `Use the Process Explorer in Views`.
- Review `Discover the Process Adherence Manager`.
"""
    (OUTPUT_DIR / "celonis_translation.md").write_text(
        translation, encoding="utf-8"
    )


def write_portfolio_readme():
    readme = """# AI-Governed CAPA Process Intelligence Case Study

## Business question

Why can a CAPA portfolio appear on target while still containing rework, late cases, and missing controls?

## Method

1. Generate a deterministic synthetic event log.
2. Calculate the headline closure KPI.
3. Discover the directly-follows process map and variants.
4. Apply transparent case-level conformance rules.
5. Use AI only for hypothesis generation and executive drafting, with human validation.

## Portfolio artifacts

- `process_map.svg`
- `variant_summary.csv`
- `directly_follows_edges.csv`
- `case_findings.csv`
- `evidence_pack.txt`
- `celonis_translation.md`

## Data statement

All records, costs, sites, severities, and timings are synthetic. No confidential or regulated data is included.
"""
    (OUTPUT_DIR / "portfolio_readme.md").write_text(readme, encoding="utf-8")


def run_full_analysis():
    metrics = headline_metrics()
    variant_rows = discover_process()
    findings = analyze_cases()
    write_evidence_pack(metrics, variant_rows, findings)
    write_celonis_translation()
    write_portfolio_readme()
    print(f"Created {OUTPUT_DIR / 'case_findings.csv'} ({len(findings)} cases)")
    print(f"Created {OUTPUT_DIR / 'evidence_pack.txt'}")
    print(f"Created {OUTPUT_DIR / 'celonis_translation.md'}")
    print(f"Created {OUTPUT_DIR / 'portfolio_readme.md'}")


def main():
    parser = argparse.ArgumentParser(
        description="Build a synthetic CAPA process intelligence portfolio case."
    )
    parser.add_argument(
        "--stage",
        choices=("generate", "baseline", "discover", "full"),
        default="full",
    )
    args = parser.parse_args()

    if args.stage == "generate":
        generate_event_log()
    elif args.stage == "baseline":
        headline_metrics()
    elif args.stage == "discover":
        ensure_event_log()
        discover_process()
    else:
        ensure_event_log()
        run_full_analysis()


if __name__ == "__main__":
    main()
  • Confirm all three project files exist by running:
ls

You should see requirements.txt, capa_process_intelligence.py, and copilot_prompt.txt in the listing.

Missing a project file?

Check that each filename matches the listing exactly. Confirm that your text editor did not append an extra .txt extension to the Python file.

Ask for help if a file is saved under the wrong name: Help me verify my CAPA project filenames.

Build the environment and generate the event log

A virtual environment isolates this project's packages from other Python projects. The script then uses those packages to create a deterministic synthetic event log.

  • Create the .venv environment with Python 3.14 by running:
py -V:3.14 -m venv .venv

PowerShell returns to the prompt after creating .venv inside C:\capa-process-intelligence.

  • Install the pinned dependencies into .venv by running:
.venv\Scripts\python.exe -m pip install -r requirements.txt

The first installation can take a few minutes while Python downloads PM4Py and its stable dependencies. PowerShell returns to the prompt when installation finishes.

Dependency installation failed?

Confirm that PowerShell is still in C:\capa-process-intelligence. Check that requirements.txt contains the exact pinned requirement.

Ask for help with the installation output: Help me install the PM4Py requirements in my virtual environment.

Before you run the generator, what should the script create from its synthetic CAPA profiles?

  • Generate the synthetic event log by running:
.venv\Scripts\python.exe capa_process_intelligence.py --stage generate

You should see a message reporting 189 events across 30 cases. The same message confirms that capa_event_log.csv was created.

What did the generator create?

The CSV contains timestamped events for CAPA-001 through CAPA-030. Each row records a case, activity, timestamp, site, severity, resource, and cost impact.

Generator did not create the CSV?

Confirm that the command uses the interpreter inside .venv. Confirm that capa_process_intelligence.py is saved in the current folder.

Ask for help with the terminal output: Help me generate the synthetic CAPA event log.

Your local process-mining workspace is ready. Next, you will inspect one CAPA case in Excel and connect its rows to the structure of an event trace.

Inspect a CAPA Event Trace

Your synthetic CAPA event log is ready with 189 events across 30 cases. Before investigating the full process, you need to understand how the rows describe one case.

Microsoft Excel gives you a clear way to isolate a case through filtering. In this step, you'll trace one CAPA record from its first activity to its closure.

In this step, get ready to:
  • Open the synthetic event log in Microsoft Excel.
  • Enable filters to isolate one CAPA case.
  • Distinguish the core event-log fields from the business-context fields.
Prepare the event log in Excel

A CSV stores table-shaped data as plain text. Excel displays each event as a row beneath the field names in capa_event_log.csv.

  • Press the Windows key to open Windows search.
  • Type Excel into the search field.
  • Press Enter to start Microsoft Excel.
  • Select File.
  • Select Open.
  • Browse to C:\capa-process-intelligence.
  • Select Text Files in the file type list.
  • Double-click capa_event_log.csv.

Good progress. You can now see the generated events arranged beneath eight column headings.

  • Select any populated cell in the data range.
  • Select the Data tab.
  • Select Filter.

You'll see a drop-down arrow beside each column heading. These controls confirm that filtering is enabled across the event log.

Missing the Filter Arrows?

  • Select a populated cell inside the event data.
  • Select Data again.
  • Select Filter again.

Ask for help if the arrows still do not appear: Help me enable filters for my CSV event log in Microsoft Excel.

Trace CAPA-001 from start to closure

A case ID groups every event belonging to the same process instance. Reading those events by timestamp creates an event trace for that case.

Before you apply the filter, how many events do you think one CAPA case will contain?

  • Select the drop-down arrow beside case_id.
  • Clear the (Select All) checkbox.
  • Select the CAPA-001 checkbox.
  • Select OK.

You'll see six rows in ascending timestamp order. Each row represents one activity performed during the case.

What Does CAPA-001 Show?

  • The trace starts with Nonconformance Logged.
  • The second activity is Containment Applied.
  • The third activity is Root Cause Analyzed.
  • The fourth activity is Corrective Action Implemented.
  • The fifth activity is Effectiveness Verified.
  • The trace finishes with CAPA Closed.
  • Read the six values in the activity column from top to bottom.
  • Compare the six values in the timestamp column from top to bottom.

The timestamps move forward as the activities progress. This ordering proves that the rows form one chronological CAPA trace.

Separate process structure from business context

The core fields reconstruct the flow of each case. The remaining fields add context that helps an analyst interpret the operational impact.

  • Select the filter button beside case_id.
  • Select Clear Filter From "case_id".
  • Scan the case_id column across several rows.
  • Scan the activity column across several rows.
  • Scan the timestamp column across several rows.
  • Inspect the four business-context columns beside the core event fields.

Core Fields and Business Context

  • The case_id field groups events that belong to the same CAPA case.
  • The activity field records what happened at each event.
  • The timestamp field establishes the event sequence.
  • The site field identifies the plant associated with the case.
  • The severity field describes the synthetic impact category.
  • The resource field identifies the role associated with an activity.
  • The cost_impact field adds a synthetic financial measure for later prioritization.

Before you restore the filter, do you expect the activity order for CAPA-001 to change?

  • Select the drop-down arrow beside case_id.
  • Clear the (Select All) checkbox.
  • Select the CAPA-001 checkbox.
  • Select OK.

You'll again see six rows in ascending timestamp order. The trace starts with Nonconformance Logged before finishing with CAPA Closed.

You can now read one case as a sequence of timestamped events. Next, you'll test whether a portfolio average tells the whole story.

Test the Headline KPI

Your inspection of CAPA-001 showed how timestamped rows form a complete journey through the event log. Each row now has a clear place in the case.

Managers often compress many case journeys into an average. A healthy-looking KPI can still leave the process paths behind that number unexplained.

In this step, get ready to:
  • Run the baseline analysis across all CAPA cases.
  • Record the case count plus the closure-time metrics.
  • Define which operational questions the headline cannot answer.
Establish the management lens

The baseline stage uses headline_metrics() to summarize closure times across the portfolio. It compares the average with the synthetic target defined by SYNTHETIC_TARGET_DAYS.

Before you run it, predict whether the headline status lands on target or off target.

  • Switch back to the PowerShell window from earlier.
  • Run the baseline analysis from C:\capa-process-intelligence by using this command:
.venv\Scripts\python.exe capa_process_intelligence.py --stage baseline

What Should I See?

Your terminal reports Cases: 30.

The average appears as Average closure days: 17.4. The median appears as Median closure days: 12.0.

The comparison point appears as Synthetic target: <= 20 days.

The final line reads Headline status: ON TARGET.

That baseline run is working. You now have a reproducible management snapshot covering all 30 cases.

Baseline Command Failed?

  • Confirm the PowerShell prompt shows C:\capa-process-intelligence.
  • Confirm the folder still contains .venv.
  • Confirm the folder still contains capa_process_intelligence.py.

Ask for help with the command output if the issue continues: Help me diagnose why the CAPA baseline command is failing in PowerShell.

Read the management snapshot

These five values form the management snapshot you can quote. Keeping the synthetic target beside the timing metrics gives the status its context.

  • Use the checkpoint below to capture the reported case count plus the four headline values.
Define the KPI boundary

The result looks favorable because the average meets the synthetic target. Its limits become clear when you ask what those five lines can actually prove.

Why Is ON TARGET Incomplete?

The average summarizes closure time across the portfolio.

It cannot identify skipped controls.

It cannot expose rework loops.

It cannot name the individual cases that require action.

  • Use the checkpoint below to state the limitation of the headline result.

Before you check the terminal again, predict which status an average of 17.4 days receives against a 20-day target.

  • Return to the baseline output in PowerShell.
  • Confirm the terminal shows Average closure days: 17.4.
  • Confirm the terminal shows Synthetic target: <= 20 days.
  • Confirm the terminal shows Headline status: ON TARGET.

This is the intended shortfall. The headline looks acceptable while the process behavior remains unexplained.

Your baseline KPI is reproducible. Next, you will move beyond the average to uncover the actual paths behind it.

Reveal Process Variants

The baseline showed an average closure time of 17.4 days against the synthetic 20-day target. That headline still cannot show how individual CAPA cases moved through the process.

Now you'll use process discovery to reconstruct the routes recorded in the event log. The result exposes the execution paths hidden by the average.

In this step, get ready to:
  • Run the PM4Py discovery stage to generate three analysis outputs.
  • Inspect the directly-follows graph for paths hidden by the average.
  • Confirm five process variants plus their exception edges in Excel.
Generate the process evidence

Process discovery orders each case's events by timestamp. It counts the transitions between consecutive activities across all cases.

Before you run discovery, ask yourself whether the 30 cases follow one route or split across several routes.

  • Switch back to the PowerShell window from the baseline stage.
  • Create the three discovery outputs by running this command from C:\capa-process-intelligence:
.venv\Scripts\python.exe capa_process_intelligence.py --stage discover

What Does the Discovery Stage Create?

  • The process_map.svg file uses Graphviz to render observed activity transitions as a visual map.
  • The variant_summary.csv file groups cases that followed the same complete activity sequence.
  • The directly_follows_edges.csv file records each source activity plus its next activity.
  • Check the final three creation messages in PowerShell.

You should see that process_map.svg, variant_summary.csv, plus directly_follows_edges.csv were created inside outputs. The variant message reports 5 variants.

You have replaced one summary number with evidence of five distinct execution paths.

Missing a Discovery Output?

Confirm that the current PowerShell location is C:\capa-process-intelligence. A different location prevents the relative virtual-environment path from resolving.

If SVG rendering fails, repeat the Graphviz version check from Step 1 in a newly opened PowerShell window.

Ask for help with the discovery stage.

Trace the hidden routes

A directly-follows graph shows each activity as a node. Each arrow represents a transition observed between consecutive events.

  • Press the Windows key to open the search bar.
  • Search for File Explorer.
  • Select File Explorer from the search results.
  • Click the address bar at the top of File Explorer.
  • Enter C:\capa-process-intelligence\outputs.
  • Press Enter.
  • Double-click process_map.svg to open the process map.

You'll see a graph titled CAPA Directly-Follows Graph. Its arrows show the routes observed across all 30 cases.

  • Trace the dominant route from Nonconformance Logged through to CAPA Closed.
  • Locate the direct edge from Nonconformance Logged to Root Cause Analyzed.
  • Locate the return edge from Effectiveness Verified to Root Cause Analyzed.

The direct edge to Root Cause Analyzed bypasses Containment Applied. The return edge records a rework loop after effectiveness verification.

That is the average's blind spot made visible. The process map shows exactly where the control path changes.

Process Map Not Opening?

Confirm that the file name ends with .svg. Choose a web browser if Windows asks which application should open the file.

If the file is missing, rerun the discovery command from the previous substep.

Get help opening or interpreting the map.

Verify the variants and edges

A process variant is the complete sequence of activities followed by a case. The edge table provides transition-level evidence for the exceptions visible on the map.

  • Return to the outputs folder in File Explorer.
  • Double-click variant_summary.csv to open it in Excel.
  • Count the data rows beneath the header row.
  • Find the row where variant_rank is 1.

You'll see five variant rows. The rank 1 row is the complete six-activity control path.

Its case_count is 16. Its case_percentage is 53.3.

Before you check the edge table, ask yourself whether the two exception arrows should appear as explicit source-to-target transitions.

  • Return to the outputs folder in File Explorer.
  • Double-click directly_follows_edges.csv to open it in Excel.
  • Find the row with Effectiveness Verified as the source activity.
  • Confirm that its rework target is Root Cause Analyzed.
  • Find the row with Nonconformance Logged as the source activity.
  • Confirm that its bypass target is Root Cause Analyzed.

You'll see a frequency of 5 for the rework return. You'll see a frequency of 4 for the containment bypass.

Strong work. You now have a process map plus two tables that prove the acceptable average concealed multiple routes.

Next, you'll quantify the affected cases. You'll then build an AI-governed value case from measured evidence.

Build an AI-Governed Value Case

Your process map exposed rework loops and control bypasses. That visual evidence still needs case-level counts before it can support an operational decision.

In this step, you'll apply transparent conformance rules to quantify the affected cases. The resulting evidence pack keeps every measured claim traceable to synthetic data.

Microsoft Copilot will help draft the executive brief within explicit evidence boundaries. A separate translation will connect your findings to current Celonis analysis concepts.

In this step, get ready to:
  • Quantify the cases affected by transparent deviation rules.
  • Draft an executive brief with responsible-AI boundaries.
  • Translate the project into current Celonis analysis concepts.
Quantify the affected cases

The headline average described the portfolio as a whole. Case-level rules identify the individual records that need attention.

Before you run the full analysis, consider whether the case-level rules will expose more risk than the headline KPI.

  • Switch back to the PowerShell window from earlier.
  • Generate the case-level findings and evidence files by running:
.venv\Scripts\python.exe capa_process_intelligence.py --stage full

What does the full stage create?

  • The script reruns the headline metrics and process discovery.
  • The transparent rules write one record per case to case_findings.csv.
  • The measured totals are written to evidence_pack.txt.
  • The interview translation is written to celonis_translation.md.
  • The project narrative is written to portfolio_readme.md.
  • Confirm the terminal reports that case_findings.csv contains 30 cases.
  • Confirm the terminal reports that evidence_pack.txt was created.
  • Confirm the terminal reports that celonis_translation.md was created.
  • Confirm the terminal reports that portfolio_readme.md was created.

That is the investigation layer working. Each flagged case now has a visible reason for review.

Missing an output file?

Confirm that PowerShell is using C:\capa-process-intelligence as its current folder. The command depends on the project files and virtual environment in that location.

Check that the earlier discovery outputs still exist inside outputs.

Help me diagnose a missing full-analysis output.

  • Use File Explorer to open C:\capa-process-intelligence\outputs\case_findings.csv in Excel.
  • Confirm that the table contains 30 case rows.

Each issue column represents one transparent test. Apply the filters separately so each result stays easy to interpret.

  • Filter rework to True.
  • Clear the rework filter.
  • Filter missing_containment to True.
  • Clear the missing_containment filter.
  • Filter missing_effectiveness to True.
  • Clear the missing_effectiveness filter.
  • Filter late_over_20_days to True.
  • Clear the late_over_20_days filter.

You'll find 5 rework cases. You'll find 4 cases missing containment.

You'll find 2 cases missing effectiveness verification. You'll find 8 cases above the synthetic target.

  • Filter status to Investigate.

Excel shows 14 cases with at least one deviation. This is the operational risk that the acceptable average concealed.

Draft the governed executive brief

An AI-generated explanation can introduce unsupported causes or recommendations. Your supplied prompt creates a human review boundary around every material statement.

  • Use File Explorer to navigate to C:\capa-process-intelligence.
  • Open outputs\evidence_pack.txt.
  • Confirm that the evidence pack reports 30 cases.
  • Confirm that the evidence pack reports 17.4 average closure days.
  • Confirm that the evidence pack reports 14 cases with at least one deviation.
  • Open copilot_prompt.txt from C:\capa-process-intelligence.

The evidence pack supplies measured facts. The prompt defines how the AI must label its interpretation of those facts.

  • Select all text in copilot_prompt.txt.
  • Copy the selected prompt.
  • In your browser, go to copilot.cloud.microsoft.
  • Paste the copied prompt into the Copilot message box.

The prompt ends with a place for the evidence pack. Supplying the measured text there gives Copilot the only source it may use.

  • Switch back to outputs\evidence_pack.txt.
  • Select the complete evidence pack.
  • Copy the selected evidence.
  • Return to the Copilot message box.
  • Place the cursor below the final Evidence pack: line.
  • Paste the copied evidence.
  • Press Enter to submit the message.

Copilot returns a five-sentence executive summary and a table of three findings. It also supplies hypotheses with validation data plus a 30-day action plan.

  • Confirm that every material statement uses exactly one of the labels EVIDENCE, HYPOTHESIS, or DECISION.
  • Compare every quantified statement with outputs\evidence_pack.txt.
  • Exclude every unsupported claim from your accepted brief.
  • Leave the reviewed response open in Copilot.

What makes this AI-governed?

  • The EVIDENCE label anchors a statement to a measured value.
  • The HYPOTHESIS label identifies an explanation that still needs validation.
  • The DECISION label identifies an action that needs a human owner.
  • The human review determines which claims enter the final brief.

Missing the required labels?

Confirm that you pasted the complete contents of copilot_prompt.txt before the evidence pack. The labeling rule appears in that file.

Reject the response if it introduces figures that do not appear in evidence_pack.txt.

Help me review my Copilot response for unsupported claims.

Your executive brief now has a visible governance boundary. Its measured facts remain separate from possible causes and human-owned actions.

Translate the findings into Celonis language

The translation maps your process outputs to Process Explorer and Variant Explorer. It maps your conformance findings to Process Adherence Manager.

Your AI-assisted brief has a conceptual connection to Celonis Insight Explorer. Each Celonis capability remains a distinct platform feature.

  • Use File Explorer to open C:\capa-process-intelligence\outputs\celonis_translation.md.
  • Confirm that the opening identifies the project as a PM4Py analysis using synthetic data.
  • Review each portfolio output in the capability translation table.

Before your final check, consider whether a local synthetic-data project can claim feature equivalence with the Celonis platform.

  • Open the Use and Interpret Views track in Celonis Academy.
  • Locate Use the Variant Explorer in Views in the track.
  • Locate Use the Process Explorer in Views in the track.
  • Locate Discover the Process Adherence Manager in the track.

You'll confirm that all three learning items appear in the current track. Your portfolio outputs now have a current interview translation without a claim of platform equivalence.

How should you explain the translation?

  • The event log reconstructs each case from its case ID, activity, and timestamp.
  • The process map supports a Process Explorer discussion about observed activity flows.
  • The variant table supports a Variant Explorer discussion about different case paths.
  • The case findings support a Process Adherence Manager discussion about observed behavior against an intended path.
  • The governed brief supports an Insight Explorer discussion about AI-assisted insight generation with human validation.

Your CAPA case now combines process evidence, a value hypothesis, and human-owned AI controls. Next, you will publish those artifacts as a public GitHub portfolio repository for clients and employers to review.

Publish the Portfolio on GitHub

Your evidence pack now separates measurements, hypotheses, and decisions. This final numbered step turns those artifacts into a public GitHub repository that a client or employer can review without opening your local machine.

GitHub Desktop provides a visual interface for Git. You will use it to track the portfolio files, protect local-only folders, and publish a reviewer-ready repository.

In this step, get ready to:
  • Set up GitHub Desktop and connect your GitHub account.
  • Prepare the portfolio files for public review.
  • Publish and inspect the public repository.
Set up GitHub Desktop

GitHub Desktop requires Windows 10 64-bit or later. Start by checking whether it is already available on your computer.

  • Press the Windows key to open Windows search.
  • Type GitHub Desktop.
  • Press Enter.

✔️ GitHub Desktop opens

GitHub Desktop is installed. Keep the application open while you connect your account.

ⓧ GitHub Desktop is not installed

Install GitHub Desktop from GitHub's official download page. The setup file launches the application when installation finishes.

  • Open the GitHub Desktop download page in your browser.
  • Click Download for Windows.
  • Open your Downloads folder in File Explorer.
  • Double-click the GitHub Desktop setup file.
  • Wait for GitHub Desktop to open.
  • Click File in the GitHub Desktop menu bar.
  • Click Options.
  • Select the Accounts pane.
  • Click Sign Into GitHub if your account is not already shown.
  • Click Continue With Browser.
  • Complete the GitHub sign-in in your browser.
  • Follow the browser prompt to return to GitHub Desktop.

You should see your GitHub username in the Accounts pane. That account becomes the owner of the portfolio repository.

Account not appearing?

GitHub Desktop requires browser-based authentication. Return to the browser tab and finish any sign-in or two-factor authentication prompt before reopening the application.

Ask for help if the account still does not appear: Help me authenticate GitHub Desktop on Windows.

Prepare the public repository

A public repository exposes every tracked file to anyone with the link. You will exclude the virtual environment and Python cache files before GitHub Desktop creates the first commit.

  • Create .gitignore inside C:\capa-process-intelligence with your text editor.
  • Copy this content into .gitignore:
.venv/
__pycache__/
*.pyc

What does this file protect?

  • .venv/ keeps downloaded project packages out of the repository.
  • __pycache__/ excludes generated Python cache folders.
  • *.pyc excludes compiled Python files.
  • Save .gitignore.
  • Confirm the ignore rules from PowerShell by running:
Get-Content C:\capa-process-intelligence\.gitignore

You should see the three ignore rules in PowerShell. This proves the file is saved in the project folder before version tracking begins.

Ignore rules missing?

Check that the file is named exactly .gitignore. Windows may add a hidden .txt extension when a text editor saves the file.

Ask for help if PowerShell cannot find it: Help me create a .gitignore file on Windows.

  • Create the repository landing page from the generated portfolio brief by running this command in PowerShell:
Copy-Item C:\capa-process-intelligence\outputs\portfolio_readme.md C:\capa-process-intelligence\README.md
  • Confirm both public-repository files exist by running:
Get-ChildItem C:\capa-process-intelligence\README.md, C:\capa-process-intelligence\.gitignore

You should see README.md and .gitignore in the output. GitHub displays the README on the repository's main page.

README copy failed?

Confirm that outputs\portfolio_readme.md exists. Re-run the full analysis stage if the generated portfolio brief is missing.

Ask for help with the file paths: Help me create the root README from my portfolio brief.

  • Open README.md in your text editor.
  • Confirm the data statement says every record is synthetic.
  • Review the project files for passwords, API keys, client names, or confidential records.
  • Remove any private information before continuing.
Publish and inspect the repository

Publishing makes the tracked files visible to anyone on GitHub. The repository contains synthetic portfolio data, while .gitignore keeps the local environment out of the commit.

  • Return to GitHub Desktop.
  • Click File in the menu bar.
  • Click Add Local Repository.
  • Click Choose....
  • Select the C:\capa-process-intelligence folder.
  • Click Select Folder.
  • Click the create a repository link when GitHub Desktop reports that the directory is not a Git repository.

Why create the repository here?

This initializes Git tracking inside the existing CAPA folder. Keeping the prefilled local path prevents GitHub Desktop from creating a nested duplicate folder.

  • Keep capa-process-intelligence in the Name field.
  • Enter Synthetic CAPA process-mining portfolio case with AI governance controls. in the Description field.
  • Leave the prefilled Local path unchanged.
  • Leave Git ignore set to None.
  • Leave License set to None.
  • Click Create repository.

The Changes view should list the project files without .venv or Python cache files. That absence confirms the ignore rules are working.

  • Review the checked files in the Changes view.
  • Enter Publish CAPA process intelligence portfolio in the Summary field.
  • Click Commit to main.

The commit moves to the History view. Your portfolio now has a local versioned snapshot.

  • Click Publish repository in the repository bar.
  • Keep capa-process-intelligence in the Name field.
  • Clear Keep this code private so clients and employers can open the repository.
  • Select None from the Organization menu to publish under your personal account.
  • Click Publish Repository.

GitHub Desktop uploads the commit and replaces the publish action with synchronization controls. Your repository is now available from your GitHub account.

  • Click Repository in the menu bar.
  • Click View on GitHub.
  • Confirm the repository shows the Public label beside its name.
  • Confirm the project files are visible without .venv.
  • Confirm the CAPA case study renders from README.md below the file list.
  • Copy the browser address into this field: your public GitHub repository URL.

That is the reviewer experience: one public link leads to the method, synthetic-data statement, process map, findings, and governance controls. Your portfolio is ready for a client or employer to inspect.

Repository not public?

Open the repository on GitHub and check the visibility label beside its name. If it is private, use the repository's Settings page and the Change repository visibility control in Danger Zone.

Ask for help if publishing or visibility changes fail: Help me make my GitHub portfolio repository public.

Secret mission

Build a Risk-Weighted CAPA Queue

Your case findings identify deviations across the CAPA portfolio. This mission turns those findings into a prioritized intervention queue using transparent weights that an operations owner can review.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep the portfolio available, pause your local work, or delete the local and remote copies. No paid cloud compute runs in this project.

Resources you used:

  • The complete local project lives in C:\capa-process-intelligence.
  • The public GitHub repository is available at your public GitHub repository URL.

Keep everything running

No action needed. Choose this if you want clients or employers to keep reviewing the public repository while you continue improving the local case study.

  • Leave C:\capa-process-intelligence in place.
  • Keep the public repository available for portfolio review.
  • Use README.md to guide future walkthroughs.
  • Commit future improvements through GitHub Desktop.

Pause - I'll come back to this later

Close the local applications to end this work session. Your files and public repository remain available when you return.

  • Close the project workbooks in Microsoft Excel.
  • Close the Microsoft Copilot browser tab.
  • Close the PowerShell window from earlier.
  • Close GitHub Desktop.
  • Leave the public repository published for portfolio review.

Your project is paused. The local artifacts remain unchanged, while reviewers can still open the public repository.

Delete - I don't want to use this again

Deleting both copies removes the local project and its public review link. Make sure you no longer need the repository before continuing.

  • Open your public GitHub repository URL in your browser.
  • Click Settings under the repository name.
  • Open the repository's General settings page.
  • Scroll to Danger Zone.

The deletion controls sit at the bottom of the settings page, so finding them can take a moment. GitHub asks for several confirmations because this action removes the remote repository.

  • Click Delete this repository.
  • Click I want to delete this repository.
  • Click I have read and understand these effects.
  • Type capa-process-intelligence in the confirmation field.
  • Click Delete this repository.

The public repository page should no longer be available. That confirms the remote portfolio copy has been removed.

The local folder is separate from the remote repository. Remove it from Windows with one PowerShell command.

  • Close every project file and GitHub Desktop.
  • Open PowerShell through Windows search.
  • Delete the local project folder by running:
Remove-Item -Recurse -Force C:\capa-process-intelligence
  • Confirm the local folder is gone by running:
Test-Path C:\capa-process-intelligence

You should see False. Python, Graphviz, and GitHub Desktop remain installed for future projects.

Nice Work!

Nice Work!

You made it! You built an end-to-end CAPA process intelligence case study and published it as a public GitHub portfolio for clients or employers to review.

You've learned how to:

  • Built a deterministic synthetic event log that reconstructs 30 CAPA cases from 189 timestamped events.
  • Used PM4Py to expose five process variants and quantify 14 deviating cases behind an ON TARGET average.
  • Applied responsible AI controls to the executive brief, mapped the outputs to current Celonis concepts, and published a public GitHub portfolio for client or employer review.
  • Completed the Secret Mission by adding a transparent risk-weighted intervention queue that sorts all 30 cases by risk_score and assigns a reviewable priority.

Ready to quiz yourself?