Orchestrate

Report an issue

Something wrong, out of date or unclear? Tell us. Corrections are prioritized over new topics. You don't need to sign in.

0 / 2000

Don't include personal or confidential company details. Reports are reviewed with AI assistance.

Your FDE portfolio and career path

Turn your course projects into evidence a reviewer can check in ten minutes, and plan your move into forward deployed engineering from an SAP role.

Updated Nov 3, 2026Foundational 8 minDeep 38 min
Foundational layer · 8 min read

The 60-second version

A forward deployed engineer (FDE) is hired for one claim: "I can move a business number in a real process, safely." A CV can only say that. A portfolio can show it.

By now a learner on this course has working projects: a blocked sales orders triage, an invoice exception copilot, a capstone exception platform. Each one runs on made-up, SAP-shaped data and has an evaluation, a human approval step and a KPI. This topic is about turning them into evidence that a stranger can check in ten minutes.

Three things do most of the work:

  • A one-page case study per project: the problem, who feels it, what the system does, where a person approves, the numbers, and what you decided next.
  • A 3-minute demo that shows the decision, not the code.
  • Honest metrics: baseline, target, result, and a clear label when the result comes from synthetic data.

The second half of the topic is about the path itself: who hires FDEs around SAP as of November 2026, what their interviews test, and how a functional or technical SAP consultant gets there.

Why it matters to the business

For a hiring manager or a client buying an FDE engagement, the risk is paying for someone who demos well and delivers nothing. FDE work is expensive because the person sits inside the business and owns a number. A wrong hire costs months of a process team's goodwill, not just a salary.

A good portfolio lowers that risk. It shows how the candidate thinks about the parts that usually sink AI on SAP: authorizations, a human approval step, an evaluation set, a baseline measured before go-live.

For the candidate, a portfolio replaces claims with checks. "I know agents" is a claim. A page that says "the procurement copilot cut the median time to decision from 25 to under 6 minutes on synthetic data, but buyers marked only 88% of explanations correct, below the 90% guardrail, so I fixed the explanations before claiming the speed" is a check. The reader can run it.

The honest version is the stronger one. A reviewer who sees a missed guardrail reported plainly learns that the candidate will tell a sponsor bad news early. That is the behavior an FDE is paid for.

How SAP does it

SAP and its partners now hire for the role by name. As of November 2026:

  • SAP's own FDE organization. A Forward Deployed AI Engineer Expert posting in Garching, posted 1 September 2026, sits in SAP's Forward Deployed Engineering organization. It asks for 10+ years including significant customer-facing delivery, leading discovery workshops and executive presentations, and deep experience with agents, RAG, MCP, evaluation and AI observability on SAP BTP, SAP AI Core and Joule. A Forward Deployed Application/ML Engineering Expert posting in Dubai, posted 2 October 2026, asks for production LLM, RAG and agent applications and names SAP AI Core, Joule Studio, RPT-1 and Business Data Cloud as a strong advantage.
  • How SAP describes the work. An SAP News article of 13 October 2026 by SAP's Industry AI lead describes FDE as co-development with customers inside live operations, starting from the business value, with examples from Takeda and Doehler.
  • Partners. Accenture launched an FDE program with SAP in June 2026 (covered in What an SAP FDE does). Lemongrass, an SAP cloud migration firm, added FDE teams to its modernization method in August 2026.
  • The same model elsewhere. Salesforce launched an FDE partner network in April 2026 and ServiceNow and Accenture an FDE program in May 2026. Several of the firms in these programs are also large SAP partners.

Postings open and close quickly, and levels vary. Read the live posting before you tailor anything to it.

SAP certifications are a separate signal. They prove product knowledge under exam conditions; a portfolio proves you can find and solve a problem. Preparing for SAP's AI certifications compares the current exams.

What a reviewer checks in ten minutes

Most reviewers give a portfolio about ten minutes before deciding whether to talk. Design for that.

They look at What it proves Red flag
The landing page You can say what each project is for in one line A list of technologies with no business problem
One case study You frame problems as metric, baseline, target Results with no baseline, or no label on synthetic numbers
"Human in the loop" You know where AI must not act alone in SAP An agent that posts or releases documents by itself
The decision line You report bad news and act on it Every project "succeeded"
The run command A stranger can reproduce it without your keys "Contact me for access"
The 3-minute demo You can show value to a business audience Ten minutes of scrolling code

Bridges from the role you have

Most people reach FDE work from an existing SAP role. Each brings half of the job.

Coming from You already bring Close this gap first Lead with this project
Functional consultant (SD, MM, FI) Process depth, fit-to-standard, key users' trust Python, APIs, evaluation (Units 1, 5, 8) The one in your own module, with your process knowledge visible in the framing
ABAP or technical developer Data model, clean core, ABAP Cloud and RAP Discovery, problem framing, LLM engineering (Units 5, 14) One that calls a released API or RAP service, plus a framed problem
BTP or integration consultant APIs, events, identity, landscapes Retrieval, agents, evaluation (Units 7 to 9) The capstone, showing identity and integration choices
Data scientist or analyst Models, metrics, evaluation habits SAP processes and authorizations (Units 1B, 11) The late delivery predictor, with the SAP data story told well
Project manager or pre-sales Stakeholders, scoping, business cases Building: hands-on Units 1, 5, 6 One small project you built yourself, end to end

Questions to ask

If you are hiring or buying FDE work:

  • Show me one project. What was the baseline, and how was it measured?
  • Where does a person approve, and what stops the system from acting without them?
  • What was the worst result you found, and what did you do about it?
  • Which SAP standard feature did you consider before building, and why didn't it fit?
  • Can I run it myself on sample data in five minutes?

If you are the candidate, ask the employer:

  • Which business metric will I own, and who on the customer side owns it with me?
  • How much time is discovery at the customer site, and how much is building?
  • How do engagements move from prototype to production here, and who runs it after?
  • What travel and customer-site presence does the role expect?

Common misconceptions

  • "More projects make a stronger portfolio." Three well-framed projects beat ten demos. Reviewers read one case study closely, not ten.
  • "Synthetic data makes a project worthless." Reviewers know you can't publish client data. What they check is whether you label it and whether the method would hold on real data.
  • "A certification replaces a portfolio." It proves product knowledge. It doesn't prove you can find the right problem and ship a solution that people adopt.
  • "FDE interviews are coding tests." Coding is one round. Breaking a vague business problem into parts, and talking to the user, weigh as much.
  • "I can show my client project if I remove the name." Client work usually stays confidential under your contract. Rebuild the idea on made-up data instead.

Key terms

All course terms, A to Z, in the glossary

  • Portfolio: a public set of finished projects, each with a case study, a run path and a demo.
  • Case study (one page): problem, users, approach, human approval, results with sources, decision and lessons for one project.
  • Synthetic data: made-up data shaped like SAP data; results from it show the method, not a business outcome.
  • Guardrail metric: a number that must not get worse while the main KPI improves, such as explanation accuracy.
  • Decomposition interview: an interview round where you break a vague real-world problem into parts, usually with little or no code.
  • GitHub Pages: GitHub's free hosting for static web pages from a repository.

Check yourself

Pick one answer for each question. The explanation appears after you choose.

1A candidate's portfolio shows a 70% time saving with no baseline source and no data label. What should a hiring manager conclude?

Answer: D. A result without a baseline source or a data label can't be checked. Asking how it was measured tests exactly what an FDE does on a real engagement. Synthetic results are fine when labeled.

2Why is reporting a missed guardrail a strength in a portfolio?

Answer: B. An FDE owns a number and must report problems before a sponsor finds them. A plainly reported miss, with the next step, shows that behavior. A page where everything succeeded shows nothing about it.

3An ABAP developer wants to move into FDE work. Which gap should they close first?

Answer: B. Developers bring the data model and clean core already. FDE work adds finding and framing the right problem and building with LLMs, which is where their gap usually is.

4What does an SAP certification prove that a portfolio doesn't?

Answer: C. Certifications test product knowledge under exam conditions. Finding problems and getting solutions adopted are what a portfolio and references show, so the two signals complement each other.

5A consultant wants to show a strong client project in their portfolio. What is the safe approach?

Answer: C. Client work usually stays confidential under the contract, even without names. Rebuilding the idea on made-up data shows the same thinking with nothing to leak.

6Which part of an FDE interview loop usually tests discovery rather than code?

Answer: D. In a decomposition round you break a vague business problem into parts, usually with little or no code. Interviewers score structure, user empathy and scoping, which is discovery work.
Deep layer · 38 min read

Mental model

A portfolio is an evidence file for one claim: "I can move metric M in SAP process P, safely." Every piece answers a question a reviewer would otherwise have to ask you.

Reviewer's question Evidence that answers it
What problem did you solve, for whom? The case study's problem and users
Did it work? Compared with what? KPI table: baseline, target, result, source
Would it be safe in SAP? Human-in-the-loop line, authorizations, the approval in the code
Can I trust your numbers? Synthetic label, evaluation report, a guardrail metric
Can I reproduce it? A run command with a sample path, a fresh-clone test
Can you explain it to my CFO? The 3-minute demo
What do you do when it fails? The decision line and what you learned

Treat the portfolio like the systems you built in this course: a manifest of facts, a check that refuses bad evidence, and a generated page. Then the page can't drift from the facts, and a careless claim fails the check before anyone sees it.

How it works

flowchart LR
  P[Course projects<br/>orchestrate-course] -->|allowlist copy| R[sap-ai-portfolio<br/>public repo]
  M[portfolio.json<br/>facts per project] --> C{check}
  C -->|FAIL| F[fix the facts]
  C -->|OK| B[build pages]
  B --> D[docs/index.html<br/>+ case studies]
  D -->|GitHub Pages| W[owner.github.io/<br/>sap-ai-portfolio]
  R --> M

Repository structure

Set up for Unit 14 created sap-ai-portfolio as a separate public repository, filled through an allowlist. This topic adds two things to it: a facts file and a generated site.

sap-ai-portfolio/
  README.md                  what this is, how to run it, the rules
  LICENSE
  requirements.txt           only what the published projects need
  portfolio.json             the facts behind the landing page (new)
  docs/                      generated landing page and case studies (new)
    index.html
    invoice-copilot.html
  unit01/triage.py
  unit14/exception_hub.py
  projects/
    invoice-exception-copilot/
      README.md              problem, KPI, how to run each stage
      reports/eval_offline.md
      reports/kpi_report_simulated.md

Each project folder follows the Portfolio checklist in its own topic: README, architecture diagram, run instructions, evaluation report, a one-page design summary and a demo script. The landing page sits above them and links to each.

The one-page case study

A case study is not a README. The README tells someone how to run the project. The case study tells someone why it matters and whether to believe it. Keep it to the sections the generated page uses:

  1. The problem, in the process owner's words, with a number: "Buyers spend about 25 minutes per late PO line finding the cause."
  2. Who feels it: the role, not "the business".
  3. What it does: one or two sentences on what code does and what the model does.
  4. Human in the loop: who approves what, and that the system never changes SAP data alone.
  5. Results: a table with baseline, target, result and source for each KPI, including one guardrail.
  6. Decision: scale, iterate, fix first or stop, and why.
  7. Run it yourself: the folder and a command with a sample path.
  8. 3-minute demo: the steps, with seconds.
  9. What I learned: one sentence a reviewer can ask you about.

The 3-minute demo

Technical storytelling gives the structure for briefs. A demo follows the same order in four beats:

Beat Time Show
Problem ~30 s One real-looking case and what the user does today
The system at work ~60 s The output with its evidence, in the user's terms
The control ~45 s A person approves; the system can't act alone; a bad input is refused
The number ~45 s The KPI against baseline and target, and the decision

Record it once as a screen recording if you can, and link it from the README. The written steps on the case study page let a reviewer follow without sound.

Metrics that matter

Reviewers look for four properties in every number:

  • A baseline with a source. "17 minutes, shadowing of 8 orders" beats "17 minutes".
  • A target agreed before the result. A target set afterwards proves nothing.
  • A guardrail. Speed without accuracy is a liability. Every project in this course has one: explanation accuracy, acceptance rate or flags per week.
  • A source label. synthetic or measured. A measured number needs evidence a reviewer could ask about, such as "pilot log, 2 weeks, 40 cases".

Cost per case and the share of proposals people overrule are good second-line numbers. Model accuracy alone is not a business metric; put it in the evaluation report, not the headline.

What FDE interviews test

FDE hiring loops differ by company. An interview-preparation site's analysis, updated August 2026, describes these rounds at Palantir, OpenAI, Anthropic and Google. It is a third party's summary, not the companies' own pages, so treat it as a guide to the shape:

Round What it tests Portfolio piece that prepares you Course topic
Decomposition Breaking a vague real-world problem into parts; user empathy; scoping; handling pushback The problem and decision sections Discovery, problem framing
Coding Clean code tied to the user's problem Any project's code and tests Units 1, 5, 6
System design Requirements, trade-offs, running at a customer site The architecture diagram and production gap Solution design doc
Learning Extending an unfamiliar codebase, asking good questions Exercises that extend a project Every Exercise
Re-engineering Debugging unfamiliar code against expected behavior Your fixes and the evaluation reports Evaluation harness

The same source says behavioral questions run through most rounds rather than sitting in one. SAP's own postings point the same way: the Garching expert role names discovery workshops and executive presentations next to the technical stack.

An SAP-shaped decomposition prompt to practise with: "Calder Pumpworks, a fictional pump maker, says its credit team is slow. Where would you start?" A strong answer asks who is slow at what, how it is measured, which orders block and why, what SAP standard already does, what data and authorizations exist, and what a first measurable target would be. It doesn't name a model in the first ten minutes.

Bridging from an SAP role

The foundational layer's table lists the gap for each starting role. Two practical points:

  • Make your old role visible in the work. A functional consultant's case study should read like someone who has configured credit management: the right block reasons, the right document flow, the right roles. That is the rare half of the FDE profile, and SAP's postings ask for it next to the AI stack.
  • Build one thing end to end yourself. Reviewers can tell a project assembled from a tutorial from one where you made the decisions. The decision line and "what I learned" are where that shows.

Build it yourself: a portfolio landing page

You will write a small script that reads a facts file, portfolio.json, checks it for the mistakes reviewers punish, and builds a landing page plus one case study page per project. Then you publish the pages for free with GitHub Pages.

Before you start: complete Set up your computer for this course and Set up for Unit 14. They create your orchestrate-course folder with its .venv, and the public sap-ai-portfolio repository next to it. This walkthrough doesn't repeat those steps. The script uses built-in Python only, so there is nothing to install.

flowchart LR
  S1[Step 1<br/>open folder] --> S2[Step 2<br/>script]
  S2 --> S3[Step 3<br/>sample page]
  S3 --> S4[Step 4<br/>break it]
  S4 --> S5[Step 5<br/>your facts]
  S5 --> S6[Step 6<br/>build]
  S6 --> S7[Step 7<br/>publish]
  S7 --> S8[Step 8<br/>save]

What you need

  • The orchestrate-course folder and the public sap-ai-portfolio repository from Set up for Unit 14.
  • A free GitHub account (you have one from Unit 1).
  • Cost: free.
  • About 40 minutes.

Step 1: Open your course folder

  1. Open VS Code, choose File > Open Folder, and open orchestrate-course.

  2. Open a terminal: Terminal > New Terminal.

  3. If the prompt doesn't start with (.venv), turn it on:

    • Windows (PowerShell):

      .venv\Scripts\Activate.ps1
    • macOS / Linux:

      source .venv/bin/activate
  4. Check that the unit14 folder exists:

    • Windows (PowerShell):

      dir unit14
    • macOS / Linux:

      ls unit14

    If you see an error saying the folder doesn't exist, create it with mkdir unit14.

Step 2: Create the script

  1. In VS Code, right-click the unit14 folder, choose New File, name it build_portfolio_page.py, paste the code below and save.
"""Check a portfolio manifest and build a static landing page from it.

Built-in Python only. Examples:
  python unit14/build_portfolio_page.py --sample
  python unit14/build_portfolio_page.py --manifest ../sap-ai-portfolio/portfolio.json --repo ../sap-ai-portfolio
  python unit14/build_portfolio_page.py --manifest ../sap-ai-portfolio/portfolio.json --check-only
"""
import argparse
import html
import json
import os
import re
import sys
from pathlib import Path

DISCLAIMER = ("Independent learning work on made-up, SAP-shaped data. "
              "Not affiliated with or endorsed by SAP.")
SYNTHETIC_NOTE = "Synthetic data: shows the method, not a result from a real SAP system."

SAMPLE = {
    "profile": {
        "name": "Your Name",
        "headline": "SAP order-to-cash consultant moving into forward deployed engineering",
        "about": ("Eight years configuring sales and credit management. I now build small AI "
                  "services around SAP processes, each with an evaluation, a human approval "
                  "step and a business KPI."),
        "repo_url": "https://github.com/YOUR-NAME/sap-ai-portfolio",
        "links": [{"label": "GitHub", "url": "https://github.com/YOUR-NAME"}],
        "disclaimer": DISCLAIMER,
    },
    "projects": [
        {
            "id": "exception-hub",
            "title": "Exception management platform (capstone)",
            "process": "Order-to-cash, procure-to-pay, plan-to-produce",
            "folder": "unit14",
            "problem": "Six teams work SAP exceptions in six worklists; nobody sees the total or the trend.",
            "users": "Credit analysts, AP clerks, planners, buyers",
            "approach": "One case record per exception, playbooks that propose an action, one approval gate.",
            "human_in_loop": "Every proposal waits for a named person; the proposer cannot approve it.",
            "status": "sample data",
            "kpis": [
                {"name": "Credit: median minutes per blocked order", "baseline": "17",
                 "target": "10", "result": "9.4", "source": "synthetic"},
                {"name": "AP: share of proposals accepted", "baseline": "none",
                 "target": "below 97%", "result": "100%", "source": "synthetic", "guardrail": True},
            ],
            "next_step": "Fix first: AP may be rubber-stamping, and planners reject too many proposals.",
            "run": "python unit14/exception_hub.py sample",
            "demo": [
                {"seconds": 30, "show": "The business problem and the 17-minute baseline"},
                {"seconds": 60, "show": "Triage the sample cases into one inbox"},
                {"seconds": 45, "show": "Approve as a second person; show the proposer cannot"},
                {"seconds": 45, "show": "The KPI table and why the report says fix first"},
            ],
            "lessons": "The flag I nearly ignored (AP accepting everything) was the most important finding.",
        },
        {
            "id": "invoice-copilot",
            "title": "Invoice exception copilot (three-way match)",
            "process": "Procure-to-pay",
            "folder": "projects/invoice-exception-copilot",
            "problem": "Blocked supplier invoices take about 30 minutes each to explain and resolve.",
            "users": "Accounts payable clerks",
            "approach": "Code finds the match differences; the model writes the explanation; the clerk decides.",
            "human_in_loop": "Releasing or changing an invoice needs the clerk's approval; the copilot only drafts.",
            "status": "sample data",
            "kpis": [
                {"name": "Median minutes per exception", "baseline": "30", "target": "10",
                 "result": "about 8", "source": "synthetic"},
                {"name": "First-pass match rate", "baseline": "68%", "target": "80%",
                 "result": "73%", "source": "synthetic"},
                {"name": "Explanations marked correct", "baseline": "none", "target": "90%",
                 "result": "96%", "source": "synthetic", "guardrail": True},
            ],
            "next_step": "Talk to purchasing about PO prices: the largest cause is upstream.",
            "run": "python projects/invoice-exception-copilot/sap_data.py --sample",
            "demo": [
                {"seconds": 30, "show": "One blocked invoice and what the clerk does today"},
                {"seconds": 60, "show": "The copilot's explanation with the evidence"},
                {"seconds": 45, "show": "The clerk approves; the log records the decision"},
                {"seconds": 45, "show": "The three KPIs and why match rate barely moved"},
            ],
            "lessons": "Speed came from the copilot; the match rate only moves when the causes are fixed.",
        },
        {
            "id": "procurement-copilot",
            "title": "Procurement copilot for late PO lines",
            "process": "Procure-to-pay",
            "folder": "projects/procurement-copilot",
            "problem": "Buyers spend about 25 minutes per late purchase order line finding the cause.",
            "users": "Buyers",
            "approach": "Code computes the delay facts; the model explains them and proposes an action.",
            "human_in_loop": "The buyer approves any change before it reaches SAP.",
            "status": "sample data",
            "kpis": [
                {"name": "Median minutes to decision", "baseline": "25", "target": "10",
                 "result": "under 6", "source": "synthetic"},
                {"name": "Explanations marked correct", "baseline": "none", "target": "90%",
                 "result": "88%", "source": "synthetic", "guardrail": True},
            ],
            "next_step": "Fix the explanations before claiming the speed: the guardrail is missed.",
            "run": "python projects/procurement-copilot/sap_data.py --sample",
            "demo": [
                {"seconds": 30, "show": "A late PO line and the buyer's question"},
                {"seconds": 60, "show": "The explanation and the proposed action"},
                {"seconds": 30, "show": "The hidden-instructions test line being refused"},
                {"seconds": 40, "show": "The KPI and the missed guardrail"},
            ],
            "lessons": "A fast wrong answer is worse than a slow right one; the guardrail says which.",
        },
    ],
}

REQUIRED_PROFILE = ["name", "headline", "disclaimer"]
REQUIRED_PROJECT = ["id", "title", "process", "folder", "problem", "users", "approach",
                    "human_in_loop", "status", "kpis", "next_step", "run", "demo"]
STATUSES = ["sample data", "pilot", "production"]
HYPE = ["revolutionary", "game-changing", "game changing", "cutting-edge", "world-class",
        "seamless", "10x", "magic"]
SECRET_PATTERNS = [
    (re.compile(r"sk-[A-Za-z0-9_-]{16,}"), "looks like a model API key"),
    (re.compile(r"AKIA[0-9A-Z]{16}"), "looks like a cloud access key"),
    (re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"), "looks like a private key"),
    (re.compile(r"(?i)(api[_-]?key|client[_-]?secret|password)\s*[:=]\s*\S{6,}"),
     "looks like a key or password"),
]
EMAIL = re.compile(r"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}")
SLUG = re.compile(r"^[a-z0-9][a-z0-9-]{1,40}$")


def all_text(value, path="manifest"):
    """Yield (path, text) for every string in the manifest."""
    if isinstance(value, dict):
        for k, v in value.items():
            yield from all_text(v, f"{path}.{k}")
    elif isinstance(value, list):
        for i, v in enumerate(value):
            yield from all_text(v, f"{path}[{i}]")
    elif isinstance(value, str):
        yield path, value


def check(manifest, repo=None):
    """Return a list of (level, code, message). Level is OK, WARN or FAIL."""
    out = []
    prof = manifest.get("profile", {})
    missing = [k for k in REQUIRED_PROFILE if not str(prof.get(k, "")).strip()]
    out.append(("FAIL" if missing else "OK", "P01",
                f"profile is missing {', '.join(missing)}" if missing else "profile"))

    projects = manifest.get("projects", [])
    if not projects:
        out.append(("FAIL", "P02", "no projects"))
    ids = [p.get("id", "") for p in projects]
    for p in projects:
        pid = p.get("id") or "(no id)"
        miss = [k for k in REQUIRED_PROJECT if not p.get(k)]
        if miss:
            out.append(("FAIL", "P02", f"{pid}: missing {', '.join(miss)}"))
        if not SLUG.match(pid) or ids.count(pid) > 1:
            out.append(("FAIL", "P10", f"{pid}: id must be unique, lowercase letters, digits and dashes"))
        if p.get("status") and p["status"] not in STATUSES:
            out.append(("FAIL", "P02", f"{pid}: status must be one of {', '.join(STATUSES)}"))

        kpis = p.get("kpis", [])
        for k in kpis:
            name = k.get("name", "(no name)")
            gaps = [f for f in ("name", "baseline", "target", "result") if not str(k.get(f, "")).strip()]
            if gaps:
                out.append(("FAIL", "P03", f"{pid}: KPI '{name}' is missing {', '.join(gaps)}"))
            if k.get("source") not in ("synthetic", "measured"):
                out.append(("FAIL", "P03", f"{pid}: KPI '{name}' needs source synthetic or measured"))
            if k.get("source") == "measured" and not str(k.get("evidence", "")).strip():
                out.append(("FAIL", "P04", f"{pid}: KPI '{name}' says measured but gives no evidence"))
        measured = [k for k in kpis if k.get("source") == "measured" and k.get("evidence")]
        if p.get("status") in ("pilot", "production") and not measured:
            out.append(("FAIL", "P04", f"{pid}: status '{p['status']}' needs at least one measured KPI"))
        if kpis and not any(k.get("guardrail") for k in kpis):
            out.append(("WARN", "P03", f"{pid}: no guardrail KPI (what must not get worse?)"))

        if p.get("run") and "--sample" not in p["run"] and " sample" not in p["run"]:
            out.append(("FAIL", "P05", f"{pid}: run command has no sample path a stranger can try"))

        demo = p.get("demo", [])
        total = sum(int(s.get("seconds", 0)) for s in demo)
        if demo and not 60 <= total <= 180:
            out.append(("FAIL", "P06", f"{pid}: demo runs {total} s; keep it between 60 and 180"))

        if repo is not None and p.get("folder") and not (repo / p["folder"]).exists():
            out.append(("FAIL", "P09", f"{pid}: folder {p['folder']} not found in {repo}"))

    for path, text in all_text(manifest):
        for rx, why in SECRET_PATTERNS:
            if rx.search(text):
                out.append(("FAIL", "P07", f"{path}: {why}"))
        for m in EMAIL.finditer(text):
            if not text.startswith("mailto:"):
                out.append(("FAIL", "P07", f"{path}: email address {m.group(0)} (use a mailto: link in profile.links)"))
        low = text.lower()
        for word in HYPE:
            if re.search(r"(?<![a-z])" + re.escape(word) + r"(?![a-z])", low):
                out.append(("WARN", "P08", f"{path}: hype word '{word}'"))

    if repo is None:
        out.append(("WARN", "P09", "folders not checked (no --repo given)"))
    if not any(level == "FAIL" and code != "P01" for level, code, _ in out):
        out.append(("OK", "P02", f"{len(projects)} projects"))
    return out


CSS = """
:root{--bg:#fbfaf7;--fg:#1d1f21;--muted:#5b6066;--card:#ffffff;--line:#e2ded6;--accent:#0b6b5d;--warn:#8a5a00;--warnbg:#fff4dc}
@media (prefers-color-scheme:dark){:root{--bg:#15171a;--fg:#e9e7e2;--muted:#a4a8ad;--card:#1d2024;--line:#33373c;--accent:#5cc8b5;--warn:#f2c46b;--warnbg:#3a2f17}}
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--fg);font:16px/1.55 system-ui,-apple-system,Segoe UI,sans-serif}
main{max-width:960px;margin:0 auto;padding:32px 16px 64px}h1{font-size:1.9rem;margin:0 0 4px}h2{margin-top:2rem}
.muted{color:var(--muted)}a{color:var(--accent)}.grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fill,minmax(280px,1fr))}
.card{background:var(--card);border:1px solid var(--line);border-radius:10px;padding:16px}.card h3{margin:0 0 6px;font-size:1.1rem}
.tag{display:inline-block;font-size:.8rem;border:1px solid var(--line);border-radius:999px;padding:1px 8px;margin-right:4px}
.synthetic{background:var(--warnbg);color:var(--warn);border-radius:6px;padding:6px 10px;font-size:.9rem}
table{border-collapse:collapse;width:100%;font-size:.95rem}th,td{border-bottom:1px solid var(--line);padding:6px 8px;text-align:left;vertical-align:top}
code{background:var(--card);border:1px solid var(--line);border-radius:4px;padding:1px 4px;overflow-wrap:anywhere}
footer{margin-top:48px;font-size:.85rem;color:var(--muted)}
"""


def esc(value):
    return html.escape(str(value), quote=True)


def page(title, body, disclaimer):
    return (f'<!doctype html>\n<html lang="en"><head><meta charset="utf-8">'
            f'<meta name="viewport" content="width=device-width,initial-scale=1">'
            f"<title>{esc(title)}</title><style>{CSS}</style></head>"
            f"<body><main>{body}<footer>{esc(disclaimer)}</footer></main></body></html>\n")


def has_synthetic(p):
    return any(k.get("source") == "synthetic" for k in p.get("kpis", []))


def headline_kpi(p):
    k = p["kpis"][0]
    label = " (synthetic)" if k.get("source") == "synthetic" else ""
    return f"{k['name']}: {k['baseline']} to {k['result']}, target {k['target']}{label}"


def build_index(m):
    prof = m["profile"]
    links = " · ".join(f'<a href="{esc(l["url"])}">{esc(l["label"])}</a>' for l in prof.get("links", []))
    cards = []
    for p in m["projects"]:
        cards.append(
            f'<div class="card"><h3><a href="{esc(p["id"])}.html">{esc(p["title"])}</a></h3>'
            f'<p><span class="tag">{esc(p["process"])}</span><span class="tag">{esc(p["status"])}</span></p>'
            f'<p>{esc(p["problem"])}</p><p class="muted">{esc(headline_kpi(p))}</p>'
            f'<p><strong>Next:</strong> {esc(p["next_step"])}</p></div>')
    note = (f'<p class="synthetic">{esc(SYNTHETIC_NOTE)} Results marked synthetic are labelled on every page.</p>'
            if any(has_synthetic(p) for p in m["projects"]) else "")
    body = (f'<h1>{esc(prof["name"])}</h1><p class="muted">{esc(prof["headline"])}</p>'
            f'<p>{esc(prof.get("about", ""))}</p><p>{links}</p>{note}'
            f'<h2>Projects</h2><div class="grid">{"".join(cards)}</div>')
    return page(f'{prof["name"]}: portfolio', body, prof["disclaimer"])


def build_case(m, p):
    prof = m["profile"]
    repo = prof.get("repo_url", "").rstrip("/")
    folder = (f'<a href="{esc(repo)}/tree/main/{esc(p["folder"])}"><code>{esc(p["folder"])}</code></a>'
              if repo else f'<code>{esc(p["folder"])}</code>')
    rows = "".join(
        f'<tr><td>{esc(k["name"])}{" (guardrail)" if k.get("guardrail") else ""}</td><td>{esc(k["baseline"])}</td>'
        f'<td>{esc(k["target"])}</td><td>{esc(k["result"])}</td>'
        f'<td>{esc(k["source"])}{": " + esc(k["evidence"]) if k.get("evidence") else ""}</td></tr>'
        for k in p["kpis"])
    note = f'<p class="synthetic">{esc(SYNTHETIC_NOTE)}</p>' if has_synthetic(p) else ""
    demo = "".join(f'<li>{esc(s["show"])} <span class="muted">({int(s["seconds"])} s)</span></li>' for s in p["demo"])
    total = sum(int(s["seconds"]) for s in p["demo"])
    lessons = f'<h2>What I learned</h2><p>{esc(p["lessons"])}</p>' if p.get("lessons") else ""
    body = (f'<p><a href="index.html">All projects</a></p><h1>{esc(p["title"])}</h1>'
            f'<p><span class="tag">{esc(p["process"])}</span><span class="tag">{esc(p["status"])}</span></p>'
            f'<h2>The problem</h2><p>{esc(p["problem"])}</p><p><strong>Who feels it:</strong> {esc(p["users"])}</p>'
            f'<h2>What it does</h2><p>{esc(p["approach"])}</p>'
            f'<h2>Human in the loop</h2><p>{esc(p["human_in_loop"])}</p>'
            f'<h2>Results</h2>{note}<table><tr><th>KPI</th><th>Baseline</th><th>Target</th><th>Result</th>'
            f'<th>Source</th></tr>{rows}</table>'
            f'<h2>Decision</h2><p>{esc(p["next_step"])}</p>'
            f'<h2>Run it yourself</h2><p>Code: {folder}</p><p><code>{esc(p["run"])}</code></p>'
            f'<h2>3-minute demo ({total} s)</h2><ol>{demo}</ol>{lessons}')
    return page(f'{p["title"]}: {prof["name"]}', body, prof["disclaimer"])


def rel(path):
    """Show a path relative to the current folder when possible."""
    try:
        return os.path.relpath(path)
    except ValueError:
        return str(path)


def main():
    ap = argparse.ArgumentParser(description="Check a portfolio manifest and build its landing page.")
    ap.add_argument("--sample", action="store_true", help="write and build the sample manifest")
    ap.add_argument("--manifest", type=Path, help="path to your portfolio.json")
    ap.add_argument("--repo", type=Path, help="portfolio repository folder, to check project folders exist")
    ap.add_argument("--out", type=Path, help="output folder (default: docs next to the manifest)")
    ap.add_argument("--check-only", action="store_true", help="run the checks, write nothing")
    args = ap.parse_args()

    if args.sample:
        base = Path(__file__).resolve().parent / "portfolio_sample"
        base.mkdir(parents=True, exist_ok=True)
        args.manifest = base / "portfolio.json"
        args.manifest.write_text(json.dumps(SAMPLE, indent=2) + "\n", encoding="utf-8")
        print(f"Wrote sample manifest {rel(args.manifest)}")
    if not args.manifest:
        ap.error("give --manifest PATH, or --sample")
    manifest_path = args.manifest.expanduser()
    try:
        manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    except FileNotFoundError:
        sys.exit(f"Manifest not found: {manifest_path}")
    except json.JSONDecodeError as e:
        sys.exit(f"Manifest is not valid JSON: line {e.lineno}, column {e.colno}: {e.msg}")

    repo = args.repo.expanduser() if args.repo else None
    results = check(manifest, repo)
    for level, code, msg in results:
        print(f"{level:<4} {code} {msg}")
    fails = sum(1 for r in results if r[0] == "FAIL")
    warns = sum(1 for r in results if r[0] == "WARN")
    print(f"{fails} FAIL, {warns} WARN")
    if fails:
        sys.exit("Nothing written. Fix the FAIL lines and run again.")
    if args.check_only:
        return

    out = (args.out or manifest_path.parent / "docs").expanduser()
    out.mkdir(parents=True, exist_ok=True)
    (out / "index.html").write_text(build_index(manifest), encoding="utf-8")
    for p in manifest["projects"]:
        (out / f"{p['id']}.html").write_text(build_case(manifest, p), encoding="utf-8")
    print(f"Wrote {rel(out / 'index.html')} and {len(manifest['projects'])} case study pages")


if __name__ == "__main__":
    main()

Step 3: Build the sample page

  1. Run:

    python unit14/build_portfolio_page.py --sample
  2. You should see:

    Wrote sample manifest unit14/portfolio_sample/portfolio.json
    OK   P01 profile
    WARN P09 folders not checked (no --repo given)
    OK   P02 3 projects
    0 FAIL, 1 WARN
    Wrote unit14/portfolio_sample/docs/index.html and 3 case study pages

    On Windows the paths show with backslashes. The WARN is expected: the sample has no repository to check folders against.

  3. Open the page in your browser. In VS Code's Explorer, right-click unit14/portfolio_sample/docs/index.html and choose Reveal in File Explorer (Windows) or Reveal in Finder (macOS), then double-click the file. On Linux, open the file from your file manager.

  4. You should see your name placeholder, a one-line headline, a yellow note saying the results are synthetic, and three project cards. Click Procurement copilot for late PO lines. The case study shows the missed guardrail (88% against a 90% target) and the decision to fix the explanations first.

Step 4: Break it on purpose

A check you have never seen fail is a check you don't trust.

  1. Open unit14/portfolio_sample/portfolio.json in VS Code.

  2. Find "source": "synthetic" under Median minutes to decision (the procurement copilot) and change it to "source": "measured". Save.

  3. Find "status": "sample data" under the capstone (exception-hub) and change it to "status": "production". Save.

  4. Run the build on that file, without --sample (which would overwrite it):

    python unit14/build_portfolio_page.py --manifest unit14/portfolio_sample/portfolio.json
  5. You should see:

    OK   P01 profile
    FAIL P04 exception-hub: status 'production' needs at least one measured KPI
    FAIL P04 procurement-copilot: KPI 'Median minutes to decision' says measured but gives no evidence
    WARN P09 folders not checked (no --repo given)
    2 FAIL, 1 WARN
    Nothing written. Fix the FAIL lines and run again.

    Nothing was written: the page you built in Step 3 is unchanged. This is the point. A sample result can't quietly become a "measured" claim, and a project on made-up data can't call itself production.

  6. Run python unit14/build_portfolio_page.py --sample again to restore the sample.

Step 5: Write your own facts

  1. Copy the sample manifest into your portfolio repository:

    • Windows (PowerShell):

      Copy-Item unit14\portfolio_sample\portfolio.json ..\sap-ai-portfolio\portfolio.json
    • macOS / Linux:

      cp unit14/portfolio_sample/portfolio.json ../sap-ai-portfolio/portfolio.json
  2. In VS Code, open sap-ai-portfolio/portfolio.json (File > Open File). Edit it:

    • profile.name, headline and about: you, in plain words. Say where you come from ("SAP MM consultant", "ABAP developer").
    • repo_url and the GitHub link: replace YOUR-NAME with your GitHub user name.
    • projects: keep only projects you have actually copied into sap-ai-portfolio. Replace the sample numbers with the ones your own runs printed, from each project's reports/ files or the capstone's capstone_report.md.
    • run: the exact command a stranger should run first. It must include a sample path (--sample, or sample for the capstone).
    • demo: four steps whose seconds add up to between 60 and 180.
  3. Keep "source": "synthetic" on every number from the course. Use "measured" only for numbers from a real pilot you are allowed to share, and add "evidence" saying how it was measured.

Step 6: Build your page and check the folders

  1. Run the check with --repo, so it confirms every project folder exists in the portfolio:

    python unit14/build_portfolio_page.py --manifest ../sap-ai-portfolio/portfolio.json --repo ../sap-ai-portfolio
  2. With two projects whose folders exist, you should see something like:

    OK   P01 profile
    OK   P02 2 projects
    0 FAIL, 0 WARN
    Wrote ../sap-ai-portfolio/docs/index.html and 2 case study pages

    A line such as FAIL P09 procurement-copilot: folder projects/procurement-copilot not found means the project isn't in the portfolio yet. Add it to portfolio.txt and run python publish_portfolio.py --copy as in Set up for Unit 14, or remove it from portfolio.json.

  3. Open sap-ai-portfolio/docs/index.html in your browser as in Step 3 and read it as a stranger would. Fix wording in portfolio.json, never in the HTML: the next build overwrites the HTML.

Step 7: Publish with GitHub Pages

  1. Commit and push from the portfolio repository:

    cd ../sap-ai-portfolio
    git add portfolio.json docs
    git status

    git status should list only portfolio.json and files under docs/. Then:

    git commit -m "Add portfolio landing page"
    git push
    cd ../orchestrate-course
  2. In your browser, open https://github.com/YOUR-NAME/sap-ai-portfolio.

  3. Click Settings (under the repository name).

  4. In the left sidebar, in the Code, planning, and automation section, click Pages.

  5. Under Build and deployment, under Source, choose Deploy from a branch.

  6. In the branch menu (it shows None), choose main. In the folder menu next to it (it shows /(root)), choose /docs.

  7. Click Save.

  8. Wait a minute or two, then open https://YOUR-NAME.github.io/sap-ai-portfolio/. Refresh the Settings > Pages screen: when the site is live, GitHub shows its address there.

What success looks like: your landing page at that address, with your projects and the synthetic-data note.

Step 8: Save your work in Git

The script is part of your course work. Commit it in the course folder:

git add unit14/build_portfolio_page.py
git commit -m "Unit 14: portfolio landing page builder"

unit14/portfolio_sample/ is generated; you can leave it out of the commit.

How the code works

Part What it does
SAMPLE The made-up manifest: a profile and three projects with course numbers, all marked synthetic
check Runs rules P01 to P10 and returns OK, WARN or FAIL lines
P01, P02, P10 Profile and project fields are present; status is one of three words; ids are unique and safe for file names
P03, P04 Every KPI has baseline, target, result and a source; measured needs evidence; pilot or production needs a measured KPI; a warning if no guardrail
P05 The run command has a sample path a stranger can try without keys
P06 The demo adds up to 60 to 180 seconds
P07 No key-like text and no bare email address anywhere in the manifest
P08 Warns on hype words such as "revolutionary"
P09 With --repo, every project folder exists in the portfolio
build_index, build_case Write the pages; every text value is escaped with html.escape, so nothing in the manifest can inject markup; synthetic results get a visible label
main Reads options, refuses to write anything if a check fails, writes docs/ next to the manifest by default

The page has no JavaScript, no web fonts and no tracking. It respects the reader's light or dark mode setting and fits a phone screen.

If something goes wrong

What you see What it means What to do
python: command not found or 'python' is not recognized Python isn't installed or isn't on the path Redo Set up your computer. On macOS or Linux, try python3
ModuleNotFoundError A file named like a built-in module (such as json.py or html.py) sits in unit14, or you ran another script This script needs no libraries. Rename any json.py, html.py or re.py in that folder
A message about a missing API key You ran another unit's script This tool needs no key and no account
A proxy or network error You ran another unit's script, or git push was blocked This script never uses the network. For git push, see the proxy row in Set up your computer
Manifest not found The path after --manifest is wrong, or the terminal isn't in orchestrate-course Run cd ~/orchestrate-course; check ../sap-ai-portfolio/portfolio.json exists
Manifest is not valid JSON: line 12, column 5 A missing comma or quote near that line Open the file at that line; VS Code underlines the error in red
FAIL P07 ...: looks like a model API key A key is in the manifest Remove it. If it was ever pushed, revoke the key with its provider now
FAIL P06 ...: demo runs 240 s Your demo steps are too long Cut a step or shorten the seconds; three minutes is the point
The Pages address shows 404 The site isn't built yet, or the folder isn't /docs Wait two minutes; check Settings > Pages shows main and /docs; check docs/index.html is on GitHub
No Pages item in Settings, or it asks you to upgrade The repository is private on a free plan Make the portfolio public as in Set up for Unit 14, Step 7

The SAP way: what SAP and its partners hire for

There is no SAP product for this topic. The SAP-specific part is what SAP and its partners ask for, and how to show it.

What SAP's postings ask for, as of November 2026. The two SAP postings opened for this topic read like this course's units:

From the posting Where the evidence goes in your portfolio
Agents, RAG, MCP, tool calling (both postings) A project with retrieval or tools, such as the procurement copilot or the capstone
Evaluation and AI observability (Garching expert posting) Evaluation reports and the decision log in every project
Own delivery from problem framing to operations (Dubai posting) The problem, decision and production gap sections
SAP BTP, SAP AI Core, Joule (Garching, required); SAP AI Core, Joule Studio, RPT-1, Business Data Cloud (Dubai, an advantage) A note in each README on how the SAP way would host it, from each project's Production gap
Discovery workshops and executive presentations (Garching) The 3-minute demo and a briefing from technical storytelling
Significant customer-facing delivery, 10+ years (Garching expert level) Not something a portfolio can replace; client references do

The Garching posting is a senior architect-level role; the Dubai posting is an engineering expert role. Both list 0 to 10% expected travel, and both describe customer-facing work: the Dubai role embeds in customer projects, the Garching role is the main technical contact for customer executives. Entry and associate roles have also been posted; What an SAP FDE does summarizes the associate profile from earlier in 2026. Check the live posting: they change.

Where SAP's own FDE work starts. SAP's 13 October article describes starting from the customer's desired business value and working backwards, inside live operations. That is the metric-first framing of problem framing, and a portfolio built on it speaks the same language.

Partners. Large SAP partners now run FDE practices on several platforms (Salesforce's partner network names Accenture, Deloitte, Capgemini, IBM Consulting, KPMG and PwC among others), and Accenture has a program with SAP. A candidate aiming at a partner should expect the client-facing half to weigh heavily: scoping, estimating and adoption, all in Unit 14.

Certifications. SAP's AI-related certifications test product skills in practical exams; Preparing for SAP's AI certifications lists them by role. Link the certification badge from your portfolio's profile; don't let it stand in for a project.

Portfolio, certification or references

Three kinds of evidence, each convincing for a different question.

Evidence Convinces Costs you Weak at Use it when
Public portfolio Hiring managers and FDE leads asking "can they build and frame?" Time; free to host Proving client-scale delivery Always, especially when moving roles
SAP certification Recruiters and partners filtering on product skill Exam fee or subscription; preparation time Discovery, adoption, judgment A posting or partner requirement names it
Client references and confidential case summaries Senior hiring and buyers of engagements Permission from clients; your track record Anything you can't name publicly Senior roles, and once you have delivered
A live interview exercise Everyone A few hours Nothing; it is the final test Prepare with the decomposition prompt above

Most people need the first two now and the third later. The portfolio is the only one you fully control.

Architecture view

Where it sits. The portfolio is outside any SAP landscape on purpose. It runs on made-up data and reaches no SAP system:

flowchart LR
  subgraph Laptop
    C[orchestrate-course<br/>private: keys, logs]
    B[build_portfolio_page.py]
  end
  subgraph GitHub
    R[sap-ai-portfolio<br/>public repo]
    PG[GitHub Pages<br/>static site]
  end
  C -->|allowlist copy| R
  R -->|portfolio.json| B
  B -->|docs/| R
  R --> PG
  V[Reviewer] --> PG
  V -->|clone + --sample| R

Non-functional requirements that decide the design:

Requirement Target What breaks if missed
Confidentiality No keys, client names or real data in the public repo A security incident or a breached contract, in public
Honesty of numbers Every result has a source; synthetic is labeled on the page Credibility, the first time a reviewer asks
Reproducibility A fresh clone runs each project with --sample, no account "Works on my machine" in front of a hiring manager
Time to read Landing page understood in one minute, case study in three The reviewer moves on
Cost and upkeep Free hosting; rebuild in seconds after a change Stale pages, or a bill for something that should be free

Integration and identity. No SAP identity or API is involved. The only identity is your GitHub account, which pushes to the repository; visitors are anonymous. GitHub's documentation states a Pages site is public even if the repository is private, so the repository's privacy is no protection. Keys stay in the course folder's .env, behind the allowlist and the secret scan from Set up for Unit 14.

The decision to record (short ADR form from the design-doc topic):

  • Context: a public page must stay consistent with each project's results, contain nothing private, and be cheap to keep current.
  • Options: A) write the HTML by hand; B) a static site generator or hosted website builder; C) a facts file plus a small checked build script.
  • Decision: C. The facts live in portfolio.json; build_portfolio_page.py refuses to publish unlabeled or unsupported claims and writes plain HTML that GitHub Pages serves from /docs.
  • Consequences: Pages can't drift from the facts, and the checks run every time. The design is plain; anyone wanting more styling edits the CSS in the script. New sections mean changing the script, not the HTML.

What a design review will ask:

  1. What stops a key or client name from reaching the public site?
  2. How does a reviewer know which numbers are synthetic?
  3. Can a stranger run every project without your accounts?
  4. What happens to the page when a project's results change?
  5. What would you show instead for work you are not allowed to publish?

Production concerns

  • Confidentiality and IP. Client work, your employer's code and anything under NDA stay out. Check your employment contract on side projects and public code; some employers require approval. Rebuild ideas on Calder's fictional data instead.
  • Secrets. The script's P07 rule and GitHub's push protection catch common key formats; they don't catch everything. The allowlist from Set up for Unit 14 stays your main control.
  • Trademarks and endorsement. Name SAP products factually. Don't use SAP logos, and keep the disclaimer that the work is independent and not endorsed by SAP. The generated footer carries it.
  • Accuracy over time. Product names and statuses change. Date anything time-sensitive in a README ("as of November 2026, Joule Studio…"), and rebuild after each change.
  • Privacy. Commits carry an email address. Keep using the no-reply address from Set up for Unit 14. Put only contact details you are happy to make public on the page.
  • Accessibility. The generated page uses real headings, tables and links, and works at phone width. Add alt text to any image you link from READMEs.
  • Upkeep. Run the fresh-clone test from Set up for Unit 14 before you send the link to anyone. A broken run command costs more than a missing project.

Pitfalls

  • Leading with the tech stack. "LangGraph, MCP, RAG" says nothing about the business. Lead with the problem and the number.
  • Rounding a synthetic number into a claim. "Cut handling time by 45%" on a CV, from a simulated month, is a misrepresentation. Keep the label wherever the number goes.
  • Ten half-finished projects. Reviewers open one. Make sure every one is finished, or remove it.
  • Hiding the failures. A missed guardrail, honestly reported, is your best evidence of judgment.
  • A demo that is a code tour. Show the user's decision and the number; keep the code for the technical interview.
  • Copying the course word for word. Reviewers will meet other learners with the same projects. Your framing, your module knowledge, your extension in each Exercise and your lessons are what make it yours.
  • Ignoring the SAP half. A portfolio that could be about any company misses the rare part of the profile. Show SAP documents, roles, approvals and clean core choices.

Exercise

Make the capstone your lead project, and practise the interview round that tests discovery.

  1. Run the capstone's report again in the course folder, so your numbers are current:

    python unit14/exception_hub.py report
  2. Open unit14/capstone/capstone_report.md. In sap-ai-portfolio/portfolio.json, update the exception-hub project with your own numbers: the credit queue's median, baseline and target, one guardrail (AP acceptance) and the decision line the report printed. Keep "source": "synthetic".

  3. Make it the first project in the projects list, so it is the first card.

  4. Write the demo for it in four beats that add up to 150 to 180 seconds, then rehearse it once with a timer.

  5. Rebuild and check:

    python unit14/build_portfolio_page.py --manifest ../sap-ai-portfolio/portfolio.json --repo ../sap-ai-portfolio
  6. Commit and push from sap-ai-portfolio as in Step 7, then open your Pages address.

  7. Practise a decomposition round. Ask a friend or colleague to read you this prompt and play the customer for 30 minutes: "Calder Pumpworks, a fictional pump maker, says its credit team is slow. Where would you start?" Don't name a model or tool in the first ten minutes.

  8. In the course folder, create unit14/career/interview_notes.md with three headings: Questions I asked, Where I jumped to a solution, What I would ask first next time. Fill each with two or three lines.

  9. Optional: create a GitHub profile README. GitHub's docs say it needs a public repository named exactly like your user name with a README.md in its root. Put two lines and your Pages link in it.

  10. Commit the notes in the course folder:

    git add unit14/career/interview_notes.md
    git commit -m "Unit 14: portfolio lead project and decomposition practice"

Done when your Pages address shows the capstone as the first card with your own numbers labeled synthetic, the build prints 0 FAIL, the capstone's demo adds up to 150 to 180 seconds, and interview_notes.md is committed. The portfolio link is what you send with every application from now on.

Check yourself

Pick one answer for each question. The explanation appears after you choose.

1Why does the build script refuse to write any page when one check fails?

Answer: B. The page is public the moment it is pushed. Refusing to write on any FAIL means a mislabeled number or a key can't slip through while the rest of the page looks fine.

2A learner changes a sample KPI's source from synthetic to measured without adding evidence. What happens, and why?

Answer: B. P04 requires an evidence string for any measured KPI, and a measured KPI before a project may claim pilot or production. That stops a sample number from being promoted into a business claim.

3Why does the script escape every manifest value with html.escape?

Answer: B. The manifest is free text that ends up in HTML. Escaping turns characters such as < and > into text, so nothing typed into a field can add tags or scripts to the page.

4Your portfolio repository is private and you publish docs/ with GitHub Pages on a paid plan. What is true?

Answer: B. GitHub's docs state that Pages sites are public on the internet even when the repository is private. Repository privacy doesn't protect what is in docs/, which is why the checks run before publishing.

5In an FDE interview's decomposition round, you are told "the credit team is slow". What should your first ten minutes focus on?

Answer: D. Decomposition tests breaking a vague problem into parts and scoping it with the user. Naming a model or writing code first skips discovery, which is the skill the round is scoring.

6SAP's Garching expert posting asks for evaluation and AI observability. Which portfolio evidence answers that best?

Answer: C. Evaluation reports and decision logs show you measured quality and watched it over time. A badge or a list of tools names the skill without showing it in use.

7A reviewer asks for a client project you delivered last year under NDA. What is the best response?

Answer: C. Confidential work stays private even without names. You can describe it as far as your contract allows, ideally backed by a reference, and point to the public project that shows the same skills on made-up data.

Sources

Sign in to track your progress

We'll email you a one-time sign-in link. No password needed.

or

Tell us a little about you

Optional, every field. It helps us pitch answers to your questions at the right level and decide which topics to write next. It is never shown publicly, and you can change or clear it anytime from the account menu.

SAP areas you work in