Home/Unit 14 · The FDE operating system and capstone
An AI roadmap and operating model for an SAP customer
Sequence assessed AI use cases into an 18-month roadmap around data and migration dates, and set up the teams, funding and reporting that keep it honest.
After a few opportunity assessments, a company running SAP has a list: ten or twenty AI ideas, each with a value, a cost and a risk. The steering committee asks two questions. In what order do we do them? And who does the work?
The first answer is the roadmap: the ideas placed in quarters over the next 12 to 24 months. Three things decide the order, more than the size of the prize:
What each idea needs first. Shared platform, clean data, a finished migration, a recorded history to learn from.
Dates you don't control. A migration go-live, a contract renewal, a release that delivers a feature in the standard.
How much the team can actually build in each quarter.
The second answer is the operating model: the teams, roles, funding and reporting that run the roadmap. Most companies start with a small central team, often called a center of excellence (CoE). As use cases spread, they move delivery into the business areas and keep the central team for the platform, the rules and the register.
A roadmap is not a promise. It is a plan you re-run every quarter, with measured results replacing the estimates.
Without a roadmap, AI work starts where the loudest sponsor sits. Three things then go wrong.
The platform gets paid twice, or never. The first use case carries the whole platform cost and fails its business case. Or every team builds its own model access, logging and approvals.
Work collides with the SAP program. A pilot goes live on a system that is migrated three months later, and has to be rebuilt.
Nobody reports the value. Projects end, the team moves on, and a year later no one can say what the money bought.
Take the course's fictional company, Calder Pumpworks. Its German company code CP20 moves from ECC into the US company's S/4HANA system on 5 July 2027. After the assessments it has 21 backlog items. In this topic's made-up plan, 19 fit into the six quarters from January 2027 to June 2028. One is dropped because it costs more to run than it returns. One, an agent that screens job applicants, is parked until legal reviews it: recruitment is a high-risk area under the EU AI Act.
The plan needs about 413,000 USD of build work. Shared platform work is 11% of it. The expected value reaches about 354,000 USD a year by mid-2028, but the portfolio as a whole only pays back around mid-2029. That is normal for a first portfolio, and it is the number a sponsor should see before approving it. If the data layer from the reporting program slips one quarter, two use cases go live a quarter later, a quarter of team time sits idle, and payback moves out by a quarter too.
SAP doesn't sell an AI roadmap. It gives you catalogs to plan from, tools to record the plan, and commercial rules that shape it. As of November 2026:
SAP's own readiness checklist. An SAP Learning lesson for enterprise architects says AI adoption usually slows because foundations are missing. It lists five areas to prepare: value and business case, release scope, commercial and legal terms (such as Joule Base vs. Premium and AI Units), identity (SAP Identity Authentication as the single entry point), and governance. Under governance it recommends clear AI ownership and an AI Center of Excellence or equivalent. SAP Learning
What SAP will deliver, and when. The AI Feature Catalog lists AI features across SAP products. The SAP Road Map Explorer at roadmaps.sap.com shows planned capabilities by quarter. SAP Learning points to both for planning adoption. Check them before you build something the standard will soon deliver. SAP Learning
Where the plan and the register live.SAP LeanIX offers roadmaps for AI initiatives and an inventory of AI technologies, from AI-enabled applications to agents. SAP AI Agent Hub, part of SAP LeanIX, is described by SAP Learning as a system of record for SAP, in-house and third-party agents, mapped to the process steps they support. sap.com · SAP Learning
How AI is paid for. SAP's pricing page says base AI is included in standard cloud subscriptions. Premium AI runs on AI Units, bought annually; unused units expire after 12 months. Joule Studio and SAP Document AI are paid through SAP BTP credits. So the roadmap and the AI Unit purchase must agree on timing. SAP pricing
A time-limited offer to plan around. At SAP TechEd on 27 October 2026, SAP said running custom agents on the Joule Studio runtime is free through December 2026, as a promotion for select customers. Don't let a free period decide the order of a two-year plan. SAP News
An existing home for the team. Many SAP customers already run a Customer Center of Expertise, which SAP Support describes as a hub across IT and the business for operating SAP. An AI CoE can grow out of it rather than beside it. SAP Support
The first 12 to 18 months; few use cases; skills are scarce
A queue: approvals slow, the team becomes the bottleneck
Hub and spoke
A central team owns platform, rules and register; FDEs sit in process areas
Several areas have live use cases
Spokes drift from the standards; the hub needs authority
Federated
Each area builds its own; the center only advises
AI is routine and areas have their own engineers
Duplicate platforms, uneven governance
Microsoft's Cloud Adoption Framework gives the same advice: centralize at the start, then move the CoE to an advisory role as adoption matures. Its warning signs for the change are approval delays and knowledge bottlenecks. Microsoft CAF
Whatever the model, five roles must be named:
Role
Owns
At Calder (fictional)
Executive sponsor
Budget and priorities; chairs the steering committee
The CFO
Use-case owner
The business result of one use case, and its quarterly number
The credit manager for blocked orders
FDE
Discovery, building and adoption, close to the users
Two FDEs, one per region
Platform team
Model access, identity, logging, the shared runtime
"The highest-value idea goes first." Order is decided as much by dependencies, dates and effort. A large idea that needs a data layer waits for it.
"The first use case should pay for the platform." That makes the first case look bad and the second look free. Fund the shared platform once, justified by what it unlocks.
"A roadmap is a commitment." It is a plan re-run every quarter. Items move when facts change, and that is the point.
"A center of excellence is permanent." It is a starting shape. Kept central too long, it becomes a queue.
"Value is reported when the project closes." Value appears after go-live, slowly. Each owner reports it every quarter for as long as the use case runs.
Pick one answer for each question. The explanation appears after you choose.
1Calder's highest-value AI idea needs a data layer that another program delivers next year. Where does it go on the roadmap?
Answer: A. A dependency sets the earliest start, whatever the value. The roadmap places the idea after the data layer and makes the dependency visible, so a slip in the other program is seen as a slip in the AI plan.
2Why fund the shared AI platform as its own budget line?
Answer: A. The platform serves many use cases. Loading it onto the first one makes that case fail its business case and the later ones look free. Justify it once, by what it unlocks.
3A sponsor asks why the portfolio only pays back after the 18-month plan ends. What is the honest answer?
Answer: C. Build spend falls in the first quarters, while value starts only after each go-live and grows as people adopt it. Showing the real payback date lets the sponsor decide with the facts rather than discover them later.
4When should a central AI team move toward hub and spoke?
Answer: A. A central team is right at the start, when skills are scarce. When several areas have live use cases and wait in its queue, delivery should move into those areas while the center keeps the platform, the rules and the register.
5Who should report each use case's value every quarter?
Answer: B. The use-case owner is accountable for the business result, so they report the measured number each quarter. Usage logs show activity, not value, and a one-off report at project close comes before the value appears.
6SAP's Road Map Explorer shows a standard feature for one of your ideas in two quarters. What should the roadmap do?
Answer: D. Building what the standard will soon deliver wastes effort and adds code to maintain. Check what the planned feature covers; if it fits, plan around its release and spend the team's days elsewhere.
7Unused AI Units expire after 12 months. What does that mean for the roadmap?
Answer: D. Units bought long before the features that use them go live can expire unused. The roadmap tells you when consumption starts, so the purchase can follow it.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
A roadmap is the output of a small scheduling problem, and the operating model is the set of people who keep its inputs true.
The inputs are a scored backlog, the team's capacity per quarter, the dependencies between items, and dates you don't control. The output is a timeline and its money: build spend, run cost and expected value by quarter. Change an input, run it again, and the roadmap changes. That is why a roadmap built in a slide tool goes stale in a month, and one built from data can be re-run at every quarterly review.
The operating model answers "who keeps each input true?":
Input
Kept true by
Value and confidence per item
The use-case owner, then measured results
Effort and run cost
FDEs and the platform team, narrowed at each gate
Dependencies on platform and data
The platform team and the data program
Dates outside the AI team
The SAP program, the migration lead, procurement
Risk tier and approvals
The governance lead, through the register
Priorities and the budget
The sponsor and the steering committee
If nobody owns an input, it stays at its first guess, and the roadmap quietly turns into fiction.
flowchart LR
A[Assessments<br/>and estimates] --> B[(Backlog)]
B --> T{Triage}
T -->|parked or dropped| N[Not now list]
T --> S[Score]
S --> P[Schedule in quarters]
C[Capacity, dependencies,<br/>windows, dates] --> P
P --> O[One-page view]
P --> M[Money by quarter]
O --> R[Quarterly review]
X[(Measured value)] --> R
R -->|update inputs| B
Items needed first, and quarters to wait after them
Architecture and data review
earliest, window
First possible quarter; the last quarter with value
Program dates
funded_by, status
Who pays the build; candidate, committed or parked
The steering committee
Two kinds are special. Platform and enabler items have no value of their own. They are scheduled when something needs them, so they are never ranked against use cases. Items funded by another program (funded_by not ai), such as Calder's data layer built by the reporting program, are fixed dates the AI team waits for, not work it does.
Parked: the status says so, or the tier is high without a legal and risk assessment. Calder's applicant-screening agent is parked: the EU AI Act topic explains why recruitment is a high-risk area.
Dropped: expected value (value × confidence) doesn't cover the run cost. A chat over a report nobody runs is the usual example.
Writing these down matters as much as the plan. A dropped item that isn't recorded comes back next quarter as a side project.
The ranking number is net expected value per year, divided by effort days:
net = value_per_year x confidence - run_cost_per_year
score = net / effort_days
This is a simple form of the idea behind SAFe's weighted shortest job first (WSJF): divide the cost of delay by the job's duration, so small valuable items go first. SAFe's version scores value, time criticality and risk reduction separately, on a relative scale. This topic keeps money and days, because the earlier topics already produced them, and handles time criticality with windows and dates instead.
Three effects of this rule are worth knowing:
Small items win. Calder's intercompany check scores 3,606 per day; the blocked-orders assistant, with the largest value, scores 520. That is intended: a quick win frees value early. It is not a judgment on which idea matters more.
Confidence counts. The MRP ranking idea is worth 70,000 a year, but at 0.5 confidence it counts as 35,000 before run cost.
Sunk cost is ignored. Only the days left count. A half-built item is ranked on what remains, as SAFe's WSJF also does.
Items are placed one by one, committed items first, then items with a value window, then by score. For each item:
Every dependency is placed first. Platform items are pulled in by the first item that needs them. The item can start the quarter after its last dependency finishes, plus lag_quarters. The MRP ranking item waits two quarters after planners start recording outcomes, so there is history to learn from.
earliest pushes the start later. Joule for Augsburg's users can't start before the migration quarter.
Effort fills free capacity from the start quarter onward and may spill into the next quarter.
If the item finishes in or after its window, it has missed its value and is left out. Calder's intercompany check only has value until CP20 joins CP10's system.
The result is a plan where every placement can be explained by a rule. That is the property a steering committee needs: "why is my item in 2028?" has an answer.
Build: days used × the day rate (course value 900 USD).
Run: a quarter of each live item's yearly run cost.
Value: a quarter of value × confidence, from the quarter after the build finishes. Items people must adopt (ai and standard) count half in their first live quarter, as a simple adoption ramp.
The cumulative line, value minus build minus run, gives the portfolio payback. This topic's tool counts value only in whole quarters after go-live, so it is more cautious than the month-by-month figures in the ECC topic.
Every quarter, each owner reports a measured value for each live item, with its source (system, measured or estimate, as in the discovery topic). The report compares it with the plan for that quarter:
Status
Rule
What happens
GREEN
90% of plan or more
Nothing; very high numbers get their measurement checked
AMBER
60% to 90%
The owner explains it at the review
RED
Below 60%
The steering committee decides: fix, re-scope or retire
The thresholds are course values. Choose your own, write them down, and keep them stable, or the colors stop meaning anything. The adoption topic shows how to measure the decision behind each number rather than logins.
Team Topologies names four team types and three ways teams interact. They map neatly onto an AI operating model:
Team Topologies type
In an AI operating model
Interaction
Stream-aligned
FDEs with a process area (order-to-cash, procure-to-pay, plant)
Collaborate with users while discovering; then own the use case
Platform
Model access, identity, logging, register, shared runtime
X-as-a-Service: use cases consume it without tickets
Enabling
The CoE's coaches, early on
Facilitation: teach a spoke, then step back
Complicated subsystem
Specialists, such as a forecasting model team
Used when the math needs deep experts
Team Topologies also warns that overloaded teams make poor decisions. A central AI team that both builds every use case and runs the platform is the classic overloaded team. Moving to hub and spoke is a way to cut its load.
Funding follows the same split:
What
Funded how
Why
Shared platform
Yearly budget, as a product
It serves every use case; it needs a stable team
Use cases
By phase and gate
Each phase can stop, as in the scoping topic
AI Units and BTP credits
Central pool, timed to go-lives
One contract, consumption follows the roadmap
Benefits
Owned by the use-case owner, checked by finance
The person who changes the process owns its result
You will build a command-line tool that reads Calder's AI backlog, ranks it, places it in six quarters under capacity and dependency rules, writes a one-page executive view, and compares measured value with the plan.
Before you start: complete Set up your computer for this course and Set up for Unit 14. They create your orchestrate-course folder, its .venv and the unit14 folder. The script uses only Python's built-in modules, so there is nothing new to install and no requirements.txt change.
flowchart LR
S[sample] --> B[(backlog.csv<br/>settings.json)]
B --> SC[score]
B --> PL[plan]
PL --> MD[roadmap.md]
A[(actuals.csv)] --> RE[report]
B --> RE
In VS Code, right-click the unit14 folder, choose New File, name it roadmap.py, paste the code below and save.
"""AI roadmap builder: turn a backlog of assessed AI ideas into a quarterly roadmap.
Commands:
sample write a made-up backlog and settings for Calder Pumpworks (fictional)
score check the backlog and rank it: expected value per day of effort
plan place items in quarters (capacity, dependencies, windows); --md writes a one-page view
report compare measured value with the plan for live items (traffic lights)
Uses only Python's built-in modules. No account, no network. Every number is made up.
"""
import argparse
import csv
import json
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
DEFAULT_DIR = HERE / "roadmap"
# ---------------------------------------------------------------------------
# 1. Columns of backlog.csv
# ---------------------------------------------------------------------------
COLUMNS = ["id", "name", "kind", "owner", "value_per_year", "confidence", "effort_days",
"run_cost_per_year", "tier", "depends", "earliest", "lag_quarters", "window",
"funded_by", "status", "note"]
KINDS = ["ai", "automation", "standard", "platform", "enabler", "migration"]
TIERS = ["low", "medium", "high"]
STATUSES = ["candidate", "committed", "parked"]
NUMBERS = ["value_per_year", "confidence", "effort_days", "run_cost_per_year", "lag_quarters"]
SHARED = ["platform", "enabler"] # scheduled when something needs them, not ranked
RAMP_KINDS = ["ai", "standard"] # people must adopt them: half value in the first live quarter
# ---------------------------------------------------------------------------
# 2. Made-up sample: Calder's AI backlog after the assessments (course values, USD)
# ---------------------------------------------------------------------------
SAMPLE_SETTINGS = {
"company": "Calder Pumpworks (fictional)",
"quarters": ["2027-Q1", "2027-Q2", "2027-Q3", "2027-Q4", "2028-Q1", "2028-Q2"],
"capacity_days": {"2027-Q1": 90, "2027-Q2": 140, "2027-Q3": 70, "2027-Q4": 160,
"2028-Q1": 160, "2028-Q2": 160},
"capacity_note": "AI team: product owner, 2 FDEs, 1 platform engineer; 2027-Q3 cut for the CP20 cutover and hypercare",
"day_rate": 900,
"funded_here": "ai",
"milestones": {"2027-Q3": "CP20 goes live in CP10's S/4HANA system (5 July 2027)"},
}
SAMPLE_BACKLOG = [
# id, name, kind, owner, value, conf, effort, run, tier, depends, earliest, lag, window, funded_by, status, note
["AI01", "Flag intercompany price mismatches (CP20/CP10)", "automation", "Intercompany team",
20700, 0.9, 5, 600, "low", "", "", 0, "2027-Q2", "ai", "candidate",
"E03 from the pre-migration register; value ends when CP20 joins CP10's system"],
["AI02", "Extract Augsburg supplier invoices with document AI", "ai", "Accounts payable CP20",
43200, 0.8, 10, 6000, "medium", "", "", 0, "", "ai", "committed",
"E01; in build; the last 10 days fall in 2027-Q1"],
["AI03", "Move invoice extraction to the S/4HANA adapter", "enabler", "Accounts payable CP20",
0, 1.0, 3, 0, "medium", "AI02", "2027-Q3", 0, "", "ai", "committed",
"the adapter rewrite from E01; must follow the CP20 go-live"],
["AI04", "Find duplicate suppliers and customers before the mock loads", "migration", "Migration lead",
0, 1.0, 8, 0, "low", "", "2027-Q1", 0, "", "migration", "committed",
"E05; the value is in the migration, so the migration budget pays"],
["AI05", "Blocked sales orders assistant for credit analysts", "ai", "Credit management",
84000, 0.6, 70, 14000, "medium", "PL1;PL2", "", 0, "", "ai", "candidate",
"assessed PROTOTYPE; P80 from the engagement estimate"],
["AI06", "Invoice exception decision table (after the price fix)", "automation", "Accounts payable CP10",
52000, 0.85, 15, 1500, "low", "", "", 0, "", "ai", "candidate",
"assessed AUTOMATE; no AI needed"],
["AI07", "Late-delivery risk for purchased components", "ai", "Purchasing",
60000, 0.5, 40, 8000, "low", "PL3", "", 0, "", "ai", "candidate",
"needs history from the data layer"],
["AI08", "Month-end variance explanations for controllers", "ai", "Controlling",
30000, 0.6, 45, 9000, "medium", "PL1;PL3", "", 0, "", "ai", "candidate", ""],
["AI09", "Rank MRP exceptions for planners", "ai", "Production planning",
70000, 0.5, 35, 6000, "low", "PL4", "", 2, "", "ai", "candidate",
"assessed PARK until outcomes are recorded; 2 quarters of labels after PL4"],
["AI10", "Maintenance notification triage at Rockford", "ai", "Maintenance planning",
36000, 0.7, 30, 5000, "low", "PL1", "", 0, "", "ai", "candidate", ""],
["AI11", "Customer email to draft sales order", "ai", "Customer service",
75000, 0.55, 55, 12000, "medium", "PL1;PL2", "", 0, "", "ai", "candidate",
"a person approves every order before it is saved"],
["AI12", "Joule for CP10 users (base AI)", "standard", "CIO office",
40000, 0.6, 12, 0, "low", "PL1", "", 0, "", "ai", "candidate",
"included in the subscription; effort is identity, roles and training"],
["AI13", "Joule for Augsburg users after the move", "standard", "CIO office",
20000, 0.6, 6, 0, "low", "AI12", "2027-Q3", 0, "", "ai", "candidate",
"CP20's users arrive in CP10's system in July 2027"],
["AI14", "Spare-parts demand forecast for the aftermarket", "ai", "Aftermarket sales",
45000, 0.5, 50, 7000, "low", "PL3", "", 0, "", "ai", "candidate", ""],
["AI15", "Service billing release assistant", "ai", "Service management",
28000, 0.6, 35, 5000, "medium", "PL1", "", 0, "", "ai", "candidate", ""],
["AI16", "Chat over Augsburg's open PO aging report", "ai", "Purchasing CP20",
3000, 0.5, 12, 4000, "low", "PL1", "", 0, "", "ai", "candidate",
"E02; zero runs of the report in 14 months"],
["AI17", "Screen Augsburg job applicants with an agent", "ai", "HR CP20",
25000, 0.5, 40, 6000, "high", "PL1;PL2", "", 0, "", "ai", "parked",
"recruitment is an Annex III high-risk area; needs a legal assessment first"],
["PL1", "AI platform foundation (BTP, generative AI hub, identity, logging)", "platform", "Platform team",
0, 1.0, 30, 18000, "low", "", "", 0, "", "ai", "candidate", ""],
["PL2", "Use-case register and agent inventory", "platform", "Governance lead",
0, 1.0, 10, 2000, "low", "", "", 0, "", "ai", "candidate", ""],
["PL3", "Data layer: CP10 history in Datasphere", "platform", "Data team",
0, 1.0, 60, 0, "low", "", "2027-Q3", 0, "", "data", "committed",
"the reporting program builds it; ready in 2027-Q3"],
["PL4", "Planners record what they did with each MRP exception", "enabler", "Production planning",
0, 1.0, 8, 0, "low", "", "", 0, "", "ai", "candidate", "a process change, not a system"],
]
SAMPLE_ACTUALS = [
# id, quarter, measured_value (USD in that quarter), note
["AI01", "2027-Q2", 4900, "system: mismatches caught before invoicing"],
["AI02", "2027-Q2", 8800, "system: invoices posted without manual keying"],
["AI06", "2027-Q2", 10100, "measured: clerk time sample, 40 invoices"],
["AI10", "2027-Q3", 2100, "measured: planner time sample; 1 of 3 shifts uses it"],
["AI12", "2027-Q3", 5200, "estimate: survey of 30 users"],
]
# ---------------------------------------------------------------------------
# 3. Reading and checking
# ---------------------------------------------------------------------------
def q_index(settings, q):
"""Position of a quarter in the horizon; None if empty, -1 if before it, len if after."""
if not q:
return None
qs = settings["quarters"]
if q in qs:
return qs.index(q)
return -1 if q < qs[0] else len(qs)
def read_backlog(path):
items, errors = [], []
with open(path, newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
if not rows:
errors.append(f"{path} has no rows")
for n, row in enumerate(rows, start=2):
missing = [c for c in COLUMNS if c not in row]
if missing:
errors.append(f"line {n}: missing columns {', '.join(missing)}")
continue
it = {k: (row[k] or "").strip() for k in COLUMNS}
where = f"line {n} ({it['id'] or 'no id'})"
for k in NUMBERS:
try:
it[k] = float(it[k] or 0)
except ValueError:
errors.append(f"{where}: {k} must be a number, not '{row[k]}'")
it[k] = 0.0
for k, allowed in (("kind", KINDS), ("tier", TIERS), ("status", STATUSES)):
if it[k] not in allowed:
errors.append(f"{where}: {k} must be one of {', '.join(allowed)}")
if not 0 <= it["confidence"] <= 1:
errors.append(f"{where}: confidence must be between 0 and 1")
it["depends"] = [d.strip() for d in it["depends"].split(";") if d.strip()]
items.append(it)
ids = [i["id"] for i in items]
for i in items:
if ids.count(i["id"]) > 1:
errors.append(f"{i['id']}: id used more than once")
for d in i["depends"]:
if d not in ids:
errors.append(f"{i['id']}: depends on {d}, which is not in the backlog")
return items, sorted(set(errors))
def load(folder):
folder = Path(folder)
sp, bp = folder / "settings.json", folder / "backlog.csv"
if not sp.exists() or not bp.exists():
sys.exit(f"file not found: {bp} or {sp}. Run: python unit14/roadmap.py sample")
settings = json.loads(sp.read_text(encoding="utf-8"))
items, errors = read_backlog(bp)
for e in errors:
print("ERROR", e)
if errors:
sys.exit(1)
return settings, {i["id"]: i for i in items}
def net_value(it):
"""Expected value per year (value x confidence) minus the yearly run cost."""
return it["value_per_year"] * it["confidence"] - it["run_cost_per_year"]
def score(it):
"""Net expected value per year for each day of effort: the ranking number."""
return net_value(it) / it["effort_days"] if it["effort_days"] else 0.0
def triage(it):
"""Why an item can't be ranked, or '' if it can."""
if it["status"] == "parked":
return "parked: " + (it["note"] or "see owner")
if it["kind"] in ("ai", "automation", "standard") and net_value(it) <= 0:
return "drop: expected value doesn't cover the run cost"
if it["tier"] == "high" and it["status"] != "committed":
return "parked: high tier needs a legal and risk assessment before planning"
return ""
# ---------------------------------------------------------------------------
# 4. Scheduling
# ---------------------------------------------------------------------------
def plan(settings, items):
qs = settings["quarters"]
free = [settings["capacity_days"][q] for q in qs]
here = settings.get("funded_here", "ai")
placed, outcome = {}, {} # placed[id] = (start, finish); outcome[id] = reason if not placed
def place(iid, chain=()):
if iid in placed:
return True
if iid in outcome:
return False
if iid in chain:
outcome[iid] = "dependency loop: " + " > ".join(chain + (iid,))
return False
it = items[iid]
why = triage(it)
if why:
outcome[iid] = why
return False
start = 0
for d in it["depends"]:
if not place(d, chain + (iid,)):
outcome[iid] = f"waits for {d} ({outcome[d].split(':')[0]})"
return False
start = max(start, placed[d][1] + 1)
start += int(it["lag_quarters"])
e = q_index(settings, it["earliest"])
if e is not None:
if e >= len(qs):
outcome[iid] = "later: earliest start is after the horizon"
return False
start = max(start, e)
if it["funded_by"] != here: # another program delivers it on its own date
if start >= len(qs):
outcome[iid] = "later: delivered after the horizon"
return False
placed[iid] = (start, start)
return True
need, use, q = it["effort_days"], {}, start
while need > 0 and q < len(qs):
take = min(need, free[q])
if take > 0:
use[q] = use.get(q, 0) + take
need -= take
q += 1
if need > 0:
outcome[iid] = "later: no capacity left in the horizon"
return False
finish = max(use) if use else start
w = q_index(settings, it["window"])
if w is not None and finish >= w:
outcome[iid] = f"missed window: must be live before {it['window']}"
return False
for q, d in use.items():
free[q] -= d
placed[iid] = (min(use) if use else start, finish)
it["_use"] = use
return True
ranked = [i for i in items.values() if i["kind"] not in SHARED or i["status"] == "committed"]
ranked.sort(key=lambda i: (i["status"] != "committed", not i["window"], -score(i)))
for it in ranked:
place(it["id"])
for it in items.values(): # shared items nothing pulled in
if it["id"] not in placed and it["id"] not in outcome:
outcome[it["id"]] = "not needed: nothing in the plan depends on it"
return placed, outcome, free
def money(settings, items, placed):
"""Quarterly build spend, run cost and expected value for the placed items."""
qs, rate, here = settings["quarters"], settings["day_rate"], settings.get("funded_here", "ai")
build, run, value = [0.0] * len(qs), [0.0] * len(qs), [0.0] * len(qs)
per_item = {}
for iid, (start, finish) in placed.items():
it = items[iid]
if it["funded_by"] == here:
for q, d in it.get("_use", {}).items():
build[q] += d * rate
w = q_index(settings, it["window"])
last = len(qs) - 1 if w is None else min(w, len(qs) - 1)
v_total = 0.0
for q in range(finish + 1, len(qs)):
if it["funded_by"] == here:
run[q] += it["run_cost_per_year"] / 4
if q > last:
continue
share = 0.5 if (it["kind"] in RAMP_KINDS and q == finish + 1) else 1.0
v = it["value_per_year"] * it["confidence"] / 4 * share
value[q] += v
v_total += v
per_item[iid] = v_total
return build, run, value, per_item
def fmt(x):
return f"{x:,.0f}"
# ---------------------------------------------------------------------------
# 5. Commands
# ---------------------------------------------------------------------------
def cmd_sample(args):
folder = Path(args.dir)
folder.mkdir(parents=True, exist_ok=True)
targets = [folder / "backlog.csv", folder / "settings.json", folder / "actuals.csv"]
if any(t.exists() for t in targets) and not args.force:
sys.exit(f"Sample files already exist in {rel(folder)}. Add --force to overwrite them.")
with open(targets[0], "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(COLUMNS)
w.writerows(SAMPLE_BACKLOG)
targets[1].write_text(json.dumps(SAMPLE_SETTINGS, indent=2) + "\n", encoding="utf-8")
with open(targets[2], "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["id", "quarter", "measured_value", "note"])
w.writerows(SAMPLE_ACTUALS)
print(f"Wrote {len(SAMPLE_BACKLOG)} backlog items, settings and sample actuals to {rel(folder)}")
print("All of it is made up (Calder Pumpworks is fictional). Next: python unit14/roadmap.py score")
def cmd_score(args):
settings, items = load(args.dir)
print(f"{'ID':5} {'Kind':10} {'Tier':6} {'Net/yr':>8} {'Days':>5} {'Score':>6} Name")
rows = [i for i in items.values() if i["kind"] not in SHARED]
rows.sort(key=lambda i: (bool(triage(i)), -score(i)))
for i in rows:
why = triage(i)
s = "-" if why else fmt(score(i))
print(f"{i['id']:5} {i['kind']:10} {i['tier']:6} {fmt(net_value(i)):>8} {fmt(i['effort_days']):>5} {s:>6} {i['name']}")
if why:
print(f"{'':37}{why}")
print("\nShared items (scheduled when something needs them, not ranked):")
for i in items.values():
if i["kind"] in SHARED:
users = [x["id"] for x in items.values() if i["id"] in x["depends"]]
unlocked = sum(max(net_value(items[u]), 0) for u in users if not triage(items[u]))
print(f" {i['id']:5} {i['name']}: needed by {', '.join(users) or 'nothing'}; "
f"unlocks {fmt(unlocked)} net a year; runs at {fmt(i['run_cost_per_year'])} a year")
def timeline(settings, items, placed):
qs = settings["quarters"]
lines = [f"{'':5} " + " ".join(q[2:] for q in qs)]
order = sorted(placed, key=lambda i: (placed[i][0], placed[i][1], i))
for iid in order:
start, finish = placed[iid]
cells = []
for q in range(len(qs)):
if start <= q <= finish:
cells.append("build " if items[iid]["funded_by"] == settings.get("funded_here", "ai") else "ready ")
elif q > finish:
cells.append("live ")
else:
cells.append(". ")
lines.append(f"{iid:5} " + "".join(cells).rstrip() + f" {items[iid]['name'][:44]}")
return lines
def cmd_plan(args):
settings, items = load(args.dir)
qs = settings["quarters"]
placed, outcome, free = plan(settings, items)
build, run, value, per_item = money(settings, items, placed)
for line in timeline(settings, items, placed):
print(line)
print("\nNot in this plan:")
for iid in sorted(outcome):
print(f" {iid:5} {outcome[iid]}")
print("\nQuarter Capacity Used Build Run Value Cumulative")
cum = 0.0
payback = None
for q in range(len(qs)):
cap = settings["capacity_days"][qs[q]]
cum += value[q] - build[q] - run[q]
if payback is None and cum >= 0 and q > 0:
payback = qs[q]
print(f"{qs[q]:9} {cap:>8} {fmt(cap - free[q]):>5} {fmt(build[q]):>7} {fmt(run[q]):>7} {fmt(value[q]):>7} {fmt(cum):>9}")
plat = sum(d * settings["day_rate"] for iid in placed if items[iid]["kind"] in SHARED
for d in items[iid].get("_use", {}).values())
run_rate = sum(items[i]["value_per_year"] * items[i]["confidence"] for i in placed
if items[i]["kind"] not in SHARED and not items[i]["window"])
print(f"\nBuild {fmt(sum(build))}, of which shared platform and enablers {fmt(plat)} "
f"({plat / sum(build):.0%}). Expected value run-rate at the end: {fmt(run_rate)} a year.")
idle = [qs[q] for q in range(len(qs)) if free[q] > settings["capacity_days"][qs[q]] / 2]
if idle:
print(f"Capacity more than half unused in {', '.join(idle)}: pull work forward or run discovery to refill the backlog.")
if payback:
print(f"Portfolio pays back in {payback} (expected values, cumulative).")
else:
last_net = value[-1] - run[-1]
if last_net > 0:
more = int(-cum // last_net) + 1
payback = f"about {more} quarters after {qs[-1]}, if the last quarter's net value holds"
else:
payback = "not in sight: the last quarter's value doesn't cover its run cost"
print(f"Portfolio pays back {payback} (expected values, cumulative).")
if args.md:
write_onepage(Path(args.md), settings, items, placed, outcome, build, run, value, per_item,
plat, run_rate, payback)
print(f"Wrote {rel(Path(args.md))}")
def write_onepage(path, settings, items, placed, outcome, build, run, value, per_item, plat, run_rate, payback):
qs = settings["quarters"]
out = [f"# AI roadmap {qs[0]} to {qs[-1]}: {settings['company']}", "",
"Made-up numbers from the course sample. Values are expected values (value x confidence).", "",
"## The ask", "",
f"- Build spend over the horizon: **{fmt(sum(build))}**, of which shared platform "
f"**{fmt(plat)}** ({plat / sum(build):.0%}).",
f"- Run cost in the horizon: **{fmt(sum(run))}**.",
f"- Expected value in the horizon: **{fmt(sum(value))}**; run-rate at the end **{fmt(run_rate)} a year**.",
f"- Cumulative payback: **{payback}**.", "",
"## By quarter", "", "| Quarter | Goes live | Milestone |", "| --- | --- | --- |"]
for q in range(len(qs)):
live = [f"{i} {items[i]['name']}" for i in sorted(placed) if placed[i][1] == q - 1 and q > 0]
out.append(f"| {qs[q]} | {'; '.join(live) or '-'} | {settings.get('milestones', {}).get(qs[q], '')} |")
out += ["", "## Use cases, owners and gates", "",
"| ID | Use case | Owner | Tier | Live from | Value in horizon |", "| --- | --- | --- | --- | --- | --- |"]
for i in sorted(placed, key=lambda x: (placed[x][1], x)):
it = items[i]
if it["kind"] in SHARED:
continue
live = qs[placed[i][1] + 1] if placed[i][1] + 1 < len(qs) else "after horizon"
out.append(f"| {i} | {it['name']} | {it['owner']} | {it['tier']} | {live} | {fmt(per_item[i])} |")
out += ["", "## Not now, and why", ""]
out += [f"- **{i}** {items[i]['name']}: {outcome[i]}" for i in sorted(outcome)]
out += ["", "## Decisions for the steering committee", "",
"- Approve the shared platform items as one budget line, not inside the first use case.",
"- Confirm the owner of each use case, who reports its measured value each quarter.",
"- Agree the parked and dropped items above, so they don't come back as side projects.", "",
"Approved by: ____________________ Date: __________", ""]
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text("\n".join(out), encoding="utf-8")
def cmd_report(args):
settings, items = load(args.dir)
qs = settings["quarters"]
placed, _, _ = plan(settings, items)
ap = Path(args.actuals or Path(args.dir) / "actuals.csv")
if not ap.exists():
sys.exit(f"file not found: {ap}")
with open(ap, newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
print(f"{'ID':5} {'Quarter':8} {'Planned':>8} {'Measured':>9} {'Share':>6} Status")
for r in rows:
iid, q = r["id"], r["quarter"]
if iid not in placed:
print(f"{iid:5} {q:8} not in the plan; add it to the backlog first")
continue
it, finish = items[iid], placed[iid][1]
qi = q_index(settings, q)
if qi is None or qi < 0 or qi >= len(qs) or qi <= finish:
print(f"{iid:5} {q:8} {'':>8} {'':>9} {'':>6} not live in {q} in the plan")
continue
share = 0.5 if (it["kind"] in RAMP_KINDS and qi == finish + 1) else 1.0
planned = it["value_per_year"] * it["confidence"] / 4 * share
measured = float(r["measured_value"])
ratio = measured / planned if planned else 0
status = "GREEN" if ratio >= 0.9 else "AMBER" if ratio >= 0.6 else "RED: fix, re-scope or retire"
if ratio > 1.5:
status += " (far above plan: check how it was measured)"
print(f"{iid:5} {q:8} {fmt(planned):>8} {fmt(measured):>9} {ratio:>6.0%} {status}")
print("\nGREEN at 90% of plan or more, AMBER from 60%, RED below. The owner explains every AMBER and RED.")
def rel(p):
try:
return str(Path(p).resolve().relative_to(Path.cwd())).replace("\\", "/")
except ValueError:
return str(p)
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--dir", default=str(DEFAULT_DIR), help="folder with backlog.csv and settings.json")
sub = ap.add_subparsers(dest="cmd", required=True)
s = sub.add_parser("sample")
s.add_argument("--force", action="store_true")
sub.add_parser("score")
p = sub.add_parser("plan")
p.add_argument("--md", help="write the one-page executive view to this Markdown file")
r = sub.add_parser("report")
r.add_argument("--actuals", help="CSV with id, quarter, measured_value, note")
args = ap.parse_args()
{"sample": cmd_sample, "score": cmd_score, "plan": cmd_plan, "report": cmd_report}[args.cmd](args)
if __name__ == "__main__":
main()
Wrote 21 backlog items, settings and sample actuals to unit14/roadmap
All of it is made up (Calder Pumpworks is fictional). Next: python unit14/roadmap.py score
Open unit14/roadmap/backlog.csv in VS Code. Each row is one item. The AI items are use cases; the PL items are platform and enabler work. Several come straight from earlier topics: AI01 to AI04 are the pre-migration register's E03, E01 and E05 plus the adapter rewrite, AI05 is the blocked-orders assistant, AI06 is the invoice decision table from the assessment, and AI09 is the parked MRP idea.
Open unit14/roadmap/settings.json. It holds the six quarters, the build days the team has in each, the day rate and the migration milestone. Note that 2027-Q3 has only 70 days: the team supports the CP20 cutover.
If you see Sample files already exist, you ran this before. Add --force to overwrite them.
ID Kind Tier Net/yr Days Score Name
AI01 automation low 18,030 5 3,606 Flag intercompany price mismatches (CP20/CP10)
AI02 ai medium 28,560 10 2,856 Extract Augsburg supplier invoices with document AI
AI06 automation low 42,700 15 2,847 Invoice exception decision table (after the price fix)
AI12 standard low 24,000 12 2,000 Joule for CP10 users (base AI)
AI13 standard low 12,000 6 2,000 Joule for Augsburg users after the move
AI09 ai low 29,000 35 829 Rank MRP exceptions for planners
AI10 ai low 20,200 30 673 Maintenance notification triage at Rockford
AI07 ai low 22,000 40 550 Late-delivery risk for purchased components
AI11 ai medium 29,250 55 532 Customer email to draft sales order
AI05 ai medium 36,400 70 520 Blocked sales orders assistant for credit analysts
AI15 ai medium 11,800 35 337 Service billing release assistant
AI14 ai low 15,500 50 310 Spare-parts demand forecast for the aftermarket
AI08 ai medium 9,000 45 200 Month-end variance explanations for controllers
AI04 migration low 0 8 0 Find duplicate suppliers and customers before the mock loads
AI17 ai high 6,500 40 - Screen Augsburg job applicants with an agent
parked: recruitment is an Annex III high-risk area; needs a legal assessment first
AI16 ai low -2,500 12 - Chat over Augsburg's open PO aging report
drop: expected value doesn't cover the run cost
Shared items (scheduled when something needs them, not ranked):
AI03 Move invoice extraction to the S/4HANA adapter: needed by nothing; unlocks 0 net a year; runs at 0 a year
PL1 AI platform foundation (BTP, generative AI hub, identity, logging): needed by AI05, AI08, AI10, AI11, AI12, AI15, AI16, AI17; unlocks 130,650 net a year; runs at 18,000 a year
PL2 Use-case register and agent inventory: needed by AI05, AI11, AI17; unlocks 65,650 net a year; runs at 2,000 a year
PL3 Data layer: CP10 history in Datasphere: needed by AI07, AI08, AI14; unlocks 46,500 net a year; runs at 0 a year
PL4 Planners record what they did with each MRP exception: needed by AI09; unlocks 29,000 net a year; runs at 0 a year
Read it from the top. Small automation items lead, because their value per day is high. The two Joule items score well because the feature is included in the subscription; the effort is identity, roles and training. The blocked-orders assistant has the largest net value but sits low, because it takes 70 days.
Read the last block. PL1 unlocks about 130,650 USD of net value a year and costs 18,000 a year to run. That is the sentence that justifies the platform, and it belongs on the steering committee's slide.
python unit14/roadmap.py plan --md unit14/roadmap/roadmap.md
You should see:
27-Q1 27-Q2 27-Q3 27-Q4 28-Q1 28-Q2
AI01 build live live live live live Flag intercompany price mismatches (CP20/CP1
AI02 build live live live live live Extract Augsburg supplier invoices with docu
AI04 ready live live live live live Find duplicate suppliers and customers befor
AI06 build live live live live live Invoice exception decision table (after the
PL1 build live live live live live AI platform foundation (BTP, generative AI h
PL2 build live live live live live Use-case register and agent inventory
PL4 build live live live live live Planners record what they did with each MRP
AI10 . build live live live live Maintenance notification triage at Rockford
AI11 . build live live live live Customer email to draft sales order
AI12 . build live live live live Joule for CP10 users (base AI)
AI05 . build build live live live Blocked sales orders assistant for credit an
AI03 . . build live live live Move invoice extraction to the S/4HANA adapt
AI13 . . build live live live Joule for Augsburg users after the move
PL3 . . ready live live live Data layer: CP10 history in Datasphere
AI15 . . build build live live Service billing release assistant
AI07 . . . build live live Late-delivery risk for purchased components
AI09 . . . build live live Rank MRP exceptions for planners
AI14 . . . build live live Spare-parts demand forecast for the aftermar
AI08 . . . build build live Month-end variance explanations for controll
Not in this plan:
AI16 drop: expected value doesn't cover the run cost
AI17 parked: recruitment is an Annex III high-risk area; needs a legal assessment first
Quarter Capacity Used Build Run Value Cumulative
2027-Q1 90 78 70,200 0 0 -70,200
2027-Q2 140 140 126,000 7,025 20,028 -183,198
2027-Q3 70 70 63,000 11,275 30,996 -226,476
2027-Q4 160 160 144,000 14,775 50,102 -335,149
2028-Q1 160 11 9,900 21,275 70,940 -295,384
2028-Q2 160 0 0 23,525 86,228 -232,681
Build 413,100, of which shared platform and enablers 45,900 (11%). Expected value run-rate at the end: 353,910 a year.
Capacity more than half unused in 2028-Q1, 2028-Q2: pull work forward or run discovery to refill the backlog.
Portfolio pays back about 4 quarters after 2028-Q2, if the last quarter's net value holds (expected values, cumulative).
Wrote unit14/roadmap/roadmap.md
Read the timeline. build is a quarter with build work; ready marks an item another program delivers; live quarters carry value. Check three placements against the rules:
AI05, the blocked-orders assistant, starts in 2027-Q2 because it needs PL1 and PL2, which finish in 2027-Q1. It spills into 2027-Q3.
AI09, the MRP ranking, starts in 2027-Q4: PL4 finishes in 2027-Q1, plus one quarter, plus two quarters of recorded outcomes.
AI07, AI08 and AI14 wait for PL3, the data layer, which the reporting program delivers in 2027-Q3.
Read the money table. Build spend is high in 2027 and drops in 2028, because the backlog runs out. The tool warns about it: unused capacity is a sign that discovery has to refill the backlog, not that the team is too big.
Read the last line. The cumulative value is still negative at the end of the horizon. At the last quarter's net value, it pays back about four quarters later.
Open unit14/roadmap/roadmap.md. In VS Code, right-click the file tab and choose Open Preview to see it formatted.
It has five parts: The ask (spend, run cost, value, payback), By quarter (what goes live, and the migration milestone), Use cases, owners and gates (one owner and one tier per row), Not now, and why, and Decisions for the steering committee.
This is the page to send to a sponsor before the meeting. It names an owner for every number, and it shows what the plan leaves out. Rewrite the decisions to fit your audience; the storytelling topic covers how.
Suppose the reporting program tells you the data layer will be one quarter late.
Open unit14/roadmap/backlog.csv.
Find the line that starts with PL3,. Change 2027-Q3 to 2027-Q4 in that line (the first one, in the earliest column) and save.
Run:
python unit14/roadmap.py plan
The end of the output should now be:
AI15 . . build build live live Service billing release assistant
AI09 . . . build live live Rank MRP exceptions for planners
PL3 . . . ready live live Data layer: CP10 history in Datasphere
AI07 . . . . build live Late-delivery risk for purchased components
AI08 . . . . build live Month-end variance explanations for controll
AI14 . . . . build live Spare-parts demand forecast for the aftermar
Not in this plan:
AI16 drop: expected value doesn't cover the run cost
AI17 parked: recruitment is an Annex III high-risk area; needs a legal assessment first
Quarter Capacity Used Build Run Value Cumulative
2027-Q1 90 78 70,200 0 0 -70,200
2027-Q2 140 140 126,000 7,025 20,028 -183,198
2027-Q3 70 70 63,000 11,275 30,996 -226,476
2027-Q4 160 36 32,400 14,775 50,102 -223,549
2028-Q1 160 135 121,500 17,525 64,378 -298,196
2028-Q2 160 0 0 23,525 79,665 -242,056
Build 413,100, of which shared platform and enablers 45,900 (11%). Expected value run-rate at the end: 353,910 a year.
Capacity more than half unused in 2027-Q4, 2028-Q2: pull work forward or run discovery to refill the backlog.
Portfolio pays back about 5 quarters after 2028-Q2, if the last quarter's net value holds (expected values, cumulative).
Compare it with Step 5. The three items that need PL3 start a quarter later, and two of them, AI07 and AI14, go live a quarter later. 2027-Q4 now has 124 days without work, and payback moved out by a quarter. That is the question to bring back to the steering committee: pull other work forward into 2027-Q4, or push the data program to keep its date?
Undo the change: set PL3 back to 2027-Q3 and save, or run python unit14/roadmap.py sample --force.
Open unit14/roadmap/actuals.csv. It holds made-up measurements, as if it were October 2027, with a source note on each line.
Run:
python unit14/roadmap.py report
You should see:
ID Quarter Planned Measured Share Status
AI01 2027-Q2 4,658 4,900 105% GREEN
AI02 2027-Q2 4,320 8,800 204% GREEN (far above plan: check how it was measured)
AI06 2027-Q2 11,050 10,100 91% GREEN
AI10 2027-Q3 3,150 2,100 67% AMBER
AI12 2027-Q3 3,000 5,200 173% GREEN (far above plan: check how it was measured)
GREEN at 90% of plan or more, AMBER from 60%, RED below. The owner explains every AMBER and RED.
Read the two flagged lines. Invoice extraction at 204% of plan and Joule at 173% look like good news, but a number far above plan usually means the measurement is wrong or the plan was. The Joule figure comes from a survey, the weakest source. Maintenance triage is AMBER: only one of three shifts uses it, which is an adoption problem, not a model problem.
Define the backlog's columns and the allowed words
SHARED, RAMP_KINDS
Which kinds are pulled in by others, and which have an adoption ramp
SAMPLE_SETTINGS, SAMPLE_BACKLOG, SAMPLE_ACTUALS
Calder's made-up horizon, backlog and measurements
read_backlog, load
Read the CSV and JSON, and stop with clear errors for bad words, numbers or dependencies
net_value, score
Expected value minus run cost, and that number per day of effort
triage
Parks and drops items before scoring, with a reason
plan, inner place
Places dependencies first, applies lag, earliest and window, fills capacity, records why items were left out
money
Build, run and expected value by quarter, with the half-value first quarter
timeline, write_onepage
The terminal timeline and the Markdown executive view
cmd_report
Compares measured value with the plan and sets GREEN, AMBER or RED
The plan is deterministic: there is no randomness, so your output should match the text above exactly. If a number differs, compare your backlog.csv with the sample.
AI Feature Catalog and SAP Road Map Explorer. Before an item enters the backlog as ai, check whether the standard covers it now or soon. SAP Learning recommends both for planning adoption, and notes that Joule releases features every other week. A planned standard feature becomes a standard item with an earliest quarter, instead of a build. SAP Learning
SAP's readiness areas as backlog items. The five areas in SAP's enterprise architect lesson (value, release scope, commercial and legal, technical prerequisites, organization and governance) are a checklist for the PL rows. Identity through SAP Identity Authentication and a governance owner are on it; both are platform items in Calder's plan. SAP Learning
SAP LeanIX can hold the roadmap as initiatives with timelines, and the AI inventory with its approval and risk status, according to SAP's product page. If the customer already uses LeanIX for its application landscape, put the AI roadmap there rather than in a separate tool, so the migration and the AI plan share one view. sap.com
SAP AI Agent Hub records each agent's source, the process step it supports, its usage, benefits and costs, according to SAP Learning. That is the same information this tool's backlog and actuals hold for agents. SAP Learning's lesson doesn't state the release status; check it on the SAP Help Portal before relying on it. SAP Learning
AI Units are bought annually and unused units expire after 12 months, per SAP's pricing page, with usage and balance shown in SAP for Me. Use the roadmap's go-live quarters for premium features to size and time the purchase. SAP pricing
SAP BTP credits pay for Joule Studio and SAP Document AI, on the same page. Your PL1 run cost and any Joule Studio agents belong in that budget.
Promotions end. SAP's TechEd release says running custom agents on the Joule Studio runtime is free through December 2026 for select customers, and that Joule Studio gains controls for model usage and spend. Give every agent built during a free period a real run_cost_per_year. SAP News
Customer Center of Expertise. SAP Support's Customer COE program describes a hub across IT and the business for operating SAP. If one exists, the AI CoE is best started inside it, which matches Microsoft's advice to build on an existing CoE before creating a new team. SAP Support
Agents come from SAP, partners and in-house builds
Standard feature timing
Manual check
AI Feature Catalog, Road Map Explorer
Always: these are the source
AI Unit consumption
Run cost column
SAP for Me usage and balance
Always, for premium SAP features
Measured value
actuals.csv and owners
AI Agent Hub's benefits and costs, for agents
The hub covers your agents and owners use it
The script is not a replacement for LeanIX. It is a way to make the rules explicit before you configure any tool, and a quick check of the plan a tool produces.
Where it sits. The roadmap's platform items are real landscape components. In Calder's fictional landscape:
flowchart LR
subgraph SAP[SAP systems]
S4[CP10 S/4HANA<br/>CP20 from July 2027]
ECC[CP20 ECC<br/>until July 2027]
end
subgraph BTP[SAP BTP]
AI[PL1 AI platform<br/>generative AI hub]
IS[Integration Suite]
end
IAS[Identity Authentication]
DS[PL3 Datasphere]
REG[PL2 register<br/>LeanIX / Agent Hub]
S4 --> IS --> AI
ECC -.SLT.-> DS
S4 --> DS --> AI
IAS --> AI
AI -.records.-> REG
Non-functional requirements that decide the platform design:
Requirement
Typical target (course value)
What breaks if missed
Shared identity
Every AI app signs in through one identity service
Each use case builds its own login; agent rights can't be audited
Register coverage
100% of live AI uses registered before go-live
Unknown AI in production; no review dates
Run cost visibility
Cost per use case per month
Platform cost can't be shared out; AI Units run out unseen
Platform availability
Matches the business hours of the processes it serves
A credit analyst's assistant down at month end
Change isolation
One use case's change can't break another's
Shared prompts or configs cause cross-team incidents
Integration and identity. SAP data reaches AI items through released APIs and events via the integration layer, as in Calder's integration platform design, and history through the data layer described in the reporting architecture. Users sign in through the shared identity service, and each agent gets its own technical user with narrow roles. Every use case on the roadmap inherits these; none builds its own.
Context: 14 planned items over 18 months; six of them need model access, logging and identity; the first, alone, can't carry the platform cost.
Options: A) each use case builds its own; B) a shared AI platform funded as a product with a yearly budget; C) a shared platform charged back to the first use case.
Decision: B. The platform team owns PL1 and PL2 with a yearly budget; use cases consume them as a service.
Consequences: One place for model access, identity and logs. The platform team needs a product owner and its own roadmap. Use-case business cases show their own costs plus a share of platform run cost.
What a design review will ask:
Which use cases depend on the data layer, and what is the plan if it slips?
How is each AI item's identity and authorization handled after CP20 moves into CP10's system?
Where is the register, and how is an unregistered AI use stopped before go-live?
How is run cost split between use cases, and who sees it each month?
Which items replace or extend standard SAP features, and how does each stay clean core?
Governance gates on the roadmap. No item goes live without a register entry and the approvals its tier requires. Put the gate in the plan: a high item is parked until legal review, as the tool does. Revisit the register at every quarterly review.
Security and authorizations. Platform items carry identity and technical users for every agent. When a migration moves users, as CP20's move does, the roles for AI items are part of the migration's role design, not an afterthought; see the roles topic.
Cost. Track run cost per use case per month. Premium features consume AI Units from a yearly pool, so time the purchase to the go-live quarters. Treat promotional periods as temporary.
Evaluation of the roadmap itself. Each quarter, compare measured value and actual effort with the plan. Feed the gaps back into confidence and effort. A team whose effort is always 30% over plan should raise its estimates.
Operations. Every live item has a run owner, an on-call path and a retirement rule. Thirty live items with no owner is a support problem waiting to happen.
Clean core. Roadmap items extend SAP side by side through released interfaces. An item that needs a modification of SAP code is a design problem to solve before it enters the plan, not during the build.
Add the delivery-blocks extension you estimated in the scoping exercise to Calder's roadmap, and write the steering committee's note.
Make sure the sample is fresh:
python unit14/roadmap.py sample --force
Open unit14/roadmap/backlog.csv and add this line at the end, then save:
AI18,Extend the blocked-orders assistant to delivery blocks,ai,Credit management,30000,0.6,30,3000,medium,AI05,,0,,ai,candidate,P80 from the delivery-blocks estimate
If you did the scoping exercise, replace 30 with your P80 divided by the day rate, rounded up.
Run:
python unit14/roadmap.py plan --md unit14/roadmap/roadmap_ai18.md
Check where AI18 landed and why. It should start the quarter after AI05 finishes.
Now cut 2027-Q4 capacity from 160 to 100 in settings.json (a second program needs the FDEs), and run the plan again. Write down which items moved.
Create unit14/roadmap/steering_note.md with four lines: what changed, which items moved and by how much, the effect on payback, and one decision you are asking the committee for.
Commit the files:
git add unit14/roadmap/
git commit -m "Unit 14: roadmap with the delivery-blocks extension and a steering note"
Done whenplan shows AI18 placed after AI05, the 2027-Q4 capacity change has been run, and steering_note.md names the moved items, the payback effect and one decision. Keep the files: your portfolio's one-page case study can reuse the executive view.
For readers who want the economics behind sequencing.
The score in this topic is a shortcut for cost of delay: what it costs, per unit of time, not to have an item live. Dividing it by duration and doing the highest ratio first minimizes the total cost of delay when items compete for one team. That is the logic SAFe packages as WSJF, using relative scores for value, time criticality and risk reduction or opportunity enablement. Two refinements are worth knowing.
Time criticality is not constant. A value window, such as Calder's intercompany check, makes cost of delay jump to zero after a date. This topic models it as a hard window. A fuller model gives each item a value curve over time.
Enablement value. Platform items have no value of their own, but they enable others. SAFe counts it as "opportunity enablement" inside cost of delay. This topic instead schedules them just in time, which is simpler but can start a platform late when two cheap items both need it.
On the operating model, two sources add depth:
Team Topologies key concepts: the four team types, three interaction modes and cognitive load, which explain why a central team that builds and runs everything slows down.
Pick one answer for each question. The explanation appears after you choose.
1Why are platform items such as PL1 scheduled when something needs them instead of ranked with the use cases?
Answer: B. A platform item's value is what it unlocks. Scored on its own value per day, it would sit at the bottom and block every use case that needs it. Pulling it in with its first dependent places it just in time.
2The blocked-orders assistant has the largest net value but ranks tenth. Why?
Answer: A. The score divides net expected value by effort days. Small, valuable items come first because they free value early. A low rank means "later", not "less important", and dependencies on PL1 and PL2 would delay it anyway.
3Why does AI09, the MRP exception ranking, start in 2027-Q4 although PL4 finishes in 2027-Q1?
Answer: A. PL4 makes planners record what they did with each exception. The model needs that history, so the item waits the quarter after PL4 plus two quarters of lag. Without the lag it would start with no labels.
4The data layer slips one quarter. What does the plan show, and what should you bring to the steering committee?
Answer: C. Only items that depend on the data layer move; the freed capacity in 2027-Q4 is a decision to make. Re-running the plan shows the effect on dates and payback in minutes, which is why the backlog is kept as data.
5The quarterly report shows one item at 204% of plan. What should the owner do first?
Answer: B. A result far above plan usually means a measurement or planning error. Checking the source keeps the report trustworthy. Only after that should confidence or value in the backlog change.
6Calder's applicant-screening agent has a positive net value. Why is it not on the plan?
Answer: D. Triage comes before scoring. A high-tier item needs its legal and risk assessment first; recruitment is a high-risk area under the EU AI Act. Recording it on the "not now" list stops it from returning as a side project.
7Which way of funding the shared platform does the topic's ADR choose, and what follows from it?
Answer: B. Option B funds the platform once, as a product with its own team and roadmap. Use cases consume it as a service, and their business cases show their own costs plus a share of platform run cost, so neither the first case nor the later ones are misleading.
8You plan Joule Studio agents while the runtime is free through December 2026. How should they enter the backlog?
Answer: C. A promotional period is temporary, so planning with zero run cost overstates the net value for every year after it. Enter a realistic run cost, paid from BTP credits, and note the assumption for the next review.
Ask a question
Testing: only staff see this
Stuck on something in this layer? Ask it here. Questions are answered in the order they arrive, and the answer appears under My questions.
Sign in (free) to ask a question. You can ask anonymously.
Related topics
Picked from the terms, links and tags this topic shares with others.
Recognizing typical SAP AI adoption and provisioning obstacles (SAP Learning, Guiding AI-driven transformation as an SAP enterprise architect)— adoption slows because foundations are missing; five readiness areas: value and business case (AI Feature Catalog, SAP LeanIX), release and product scope, commercial and legal (Joule Base vs. Premium, AI Units), technical prerequisites (SAP Identity Authentication as single entry point), organization and governance (clear AI ownership, an AI Center of Excellence or equivalent, SAP AI Agent Hub, regular review of new AI features). Opened 2026-11-02
Managing AI agents using AI Agent Hub (SAP Learning)— a system of record for SAP, in-house and third-party agents within SAP LeanIX; maps agents to business capabilities and process steps; records usage, benefits and costs; executive dashboard. Opened 2026-11-02
Enterprise architecture management with SAP LeanIX (sap.com)— inventory of AI technologies from AI-enabled applications to agents; tracks AI approval and adoption; AI risk management; roadmaps for AI initiatives; prioritized initiatives with visual timelines. Opened 2026-11-02
SAP Business AI packages and pricing (sap.com)— base AI included in standard cloud subscriptions; premium AI powered by AI Units, purchased annually, unused units expire after 12 months; usage and balance in SAP for Me; Joule Studio and SAP Document AI through SAP BTP credits. Opened 2026-11-02
SAP empowers AI developers to build what matters (SAP News, 27 October 2026)— SAP TechEd: running custom agents on the Joule Studio runtime free through December 2026 as a time-bound promotion for select customers; design-time tooling free under fair-use limits; SAP Enterprise AI Gateway announced; Joule Studio observability and admin controls for model usage and spend. Opened 2026-11-02
Customer Center of Expertise (SAP Support Portal)— Customer COEs as collaboration hubs across IT and lines of business, driving transparency, efficiency and quality in implementing, innovating and operating SAP. Opened 2026-11-02
AI center of excellence (Microsoft Cloud Adoption Framework, MicrosoftDocs source)— CoE runs intake and prioritization, standards and reusable assets; centralized model early, advisory model as adoption matures; warning signs: approval delays, knowledge bottlenecks; build on an existing cloud CoE where possible. Opened 2026-11-02
Key concepts (Team Topologies)— stream-aligned, enabling, complicated-subsystem and platform teams; collaboration, X-as-a-Service and facilitation; cognitive load. Opened 2026-11-02
Weighted Shortest Job First (SAFe)— WSJF as relative cost of delay divided by relative job duration; inputs are business value, time criticality, and risk reduction or opportunity enablement; sequencing, not a standalone ROI; ignores sunk costs. Opened 2026-11-02