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.

The integration lead's toolkit: inventory, specs, testing and cutover

Run the integration workstream of an SAP program with an interface inventory, a complete spec, a test strategy, error ownership and a cutover plan.

Updated Oct 30, 2026Foundational 9 minDeep 40 min
Foundational layer · 9 min read

The 60-second version

An SAP program connects the new system to dozens of others: banks, carriers, customers, the shop floor, the CRM. Someone has to know every one of those connections, make sure each is designed, tested and watched, and switch them over on go-live weekend without losing a message. That person is the integration lead.

Their toolkit is short and unglamorous:

  • An interface inventory. One list with every interface, its owner, volume, service level, error handling and status. It is the single source of truth for the workstream.
  • A specification per interface. What the interface does, field by field, what can go wrong, who fixes it and how it is tested.
  • A test strategy. Each interface tested alone, then in a chain, then inside the whole business process, then at peak volume.
  • An error-handling design. For every kind of failure, a named team that fixes and reprocesses it.
  • A cutover plan. The hour-by-hour steps to stop, drain, switch and start interfaces, with a way back.
  • Hypercare. Close watch on every interface in the first weeks after go-live.

AI helps with the paperwork: drafting specs and mappings, proposing test cases, grouping error logs. It doesn't replace sign-off. This topic shows the toolkit on Calder Pumpworks, the fictional pump maker used across this course.

Why it matters to the business

Interfaces are where go-lives fail in public. A sales order that never reached the warehouse looks to the customer like Calder forgot them. Four things make the integration workstream costly when it is run loosely:

  • Unknown interfaces. SAP's own implementation training calls the first list of connected applications a "starting point", because nobody remembers them all; fit-to-standard workshops usually find more (SAP Learning). An interface found in the final test costs a design, a build and a test in the busiest weeks of the program.
  • Orphaned errors. When Hallam Fluid Systems, a distributor, sends a purchase order with a part number Calder doesn't recognize, the message stops. If no team owns that error, the order sits until Hallam calls. The fix is a name in the inventory, not a tool.
  • Tests that prove too little. An interface that passes its own test can still fail inside the process: the order arrives, but the delivery never reaches the warehouse. Only an end-to-end test shows the business result.
  • Cutover without a way back. Calder plans to move its German company CP20 into the US company's S/4HANA system in July 2027. If the intercompany EDI link is switched off before the go/no-go decision, a "no-go" leaves Augsburg unable to trade with Rockford.

The business decision is how much discipline to buy: an integration lead with a real inventory and sign-offs, or a team that discovers interfaces as they break.

How SAP does it

As of October 2026, from SAP pages opened for this topic:

  • SAP Activate, SAP's implementation method, places integration inside the application workstreams. Unit tests (and sometimes string tests) run in the Realize sprints; a full integration test and user acceptance testing are required before the Deploy phase; Deploy includes hypercare after go-live (SAP Learning).
  • SAP Cloud ALM holds the integration requirements: SAP's private edition training says to document them in its Requirements app once fit-to-standard ends (SAP Learning). Its Integration & Exception Monitoring follows messages across systems, finds a single message by a business value such as an order number, and raises alerts that someone assigns and confirms (SAP Support).
  • SAP Integration Suite adds design-time help: Integration Assessment for the integration strategy and interface requests, Integration Advisor for message and mapping guidelines with AI-powered mapping proposals, and Trading Partner Management for partner profiles, agreements and partner monitoring (SAP).
  • SAP Application Interface Framework (AIF) gives business power users their own view of interface errors, with correction and mass error handling, controlled by roles (SAP). It is priced separately.

None of these writes your inventory or decides who owns an error. They hold and show what the integration lead has decided.

The integration workstream on one page

Phase (SAP Activate) Integration lead's work What exists at the end Calder example
Discover, Prepare First list of systems and interfaces A draft inventory 12 interfaces from the exception trace and the target architecture
Explore Fit-to-standard finds more; pattern and route per interface Inventory with owners, patterns, volumes I13 cost centers to the HR cloud found in a workshop
Realize Specs, mappings, build, unit and string tests Approved specs; tests passed per interface Spec for I10 distributor orders
Realize (end) End-to-end and volume tests; error drills Signed test results Hallam order to invoice, twice the peak hour
Deploy Cutover steps for every changing interface; go/no-go Interfaces switched, with rollback ready I11 intercompany EDI retired after the fallback deadline
Deploy (hypercare) Daily error review with owners Error list trending to zero Unknown part numbers fixed in the first week
Run Handover to operations Monitoring and owners in production Alerts go to the named teams

Questions to ask

  1. Where is our interface inventory, who maintains it, and does every row have a business owner and an error owner?
  2. How many interfaces were added after fit-to-standard, and how many are still without an approved spec?
  3. For each interface, which test level has it passed: unit, string, end-to-end, volume?
  4. When a message fails at night, who gets the alert, and can that team fix and reprocess it themselves?
  5. Which interfaces change at cutover, and what is the rollback for each if the go/no-go is "no"?
  6. How long is hypercare, and what error rate ends it?
  7. Where do we use AI to draft specs, test cases or error summaries, and who signs off what it produced?

Common misconceptions

  • "The interface list is IT's spreadsheet." It is the program's contract: owners, service levels and error handling are business decisions.
  • "If the interface test passed, the process works." An interface test proves the message arrived. Only an end-to-end test proves the order became a delivery and an invoice.
  • "Errors go to the integration team." Technical errors do. A wrong part number is a business error; only the business can fix it correctly.
  • "Cutover means switching everything on." Some interfaces must stay untouched until the fallback deadline passes, so a "no-go" can still go back.
  • "AI can write the specs." AI can draft them quickly. A spec is a decision the owners sign; a draft nobody reviewed is a guess.

Key terms

All course terms, A to Z, in the glossary

  • Interface inventory: the single list of all interfaces with owner, pattern, volume, service level, error handling, monitoring and status.
  • Functional spec: what an interface does in business terms: trigger, content, rules, errors.
  • Technical spec: how it is built: technology, mapping, security, retries, monitoring.
  • Mapping document: a field-by-field table from source to target, with the rule for each field.
  • String test: a test of several connected steps or systems in a chain, short of the whole process.
  • Error owner: the named team that fixes and reprocesses a failed message.
  • Reprocessing: running a failed message again after the cause is fixed.
  • Cutover: the planned window when old systems stop and new ones start.
  • Hypercare: the period of close support right after go-live.

Check yourself

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

1Why does SAP's training call the first list of connected applications a "starting point"?

Answer: D. The first list comes from memory and always misses some applications. Fit-to-standard workshops uncover more, and each late discovery costs a design, build and test in the busiest weeks.

2Hallam sends an order with a part number Calder doesn't know. Who should own that error?

Answer: A. Retrying never fixes a wrong part number. The team that knows Calder's products and the distributor must decide what the part is, fix it and reprocess the message.

3An interface passed its own test. What does that still not prove?

Answer: C. An interface test stops at the boundary. Only an end-to-end test inside the business process shows the business result, such as the order being delivered and billed.

4Why must the intercompany EDI link stay usable until the fallback deadline at Calder's cutover?

Answer: A. Until the fallback deadline passes, ECC may reopen. If the link is already gone, a "no-go" restores Augsburg's system but not its trade with Rockford.

5What is the right way to use AI for interface specs?

Answer: A. Drafting is where AI saves time. The spec records decisions about owners, rules and errors; those still need the people accountable for them to review and sign.

6Which question best tests whether the integration workstream is under control?

Answer: A. Headcount and licenses say little about readiness. An inventory where every row has owners and a current test status shows what is ready, what is missing and who acts.
Deep layer · 40 min read

Mental model

Treat every interface as a small product with a life cycle, and the inventory as the board that shows where each one is:

flowchart LR
  F[Found] --> D[Designed:<br/>pattern, owner]
  D --> S[Specified:<br/>spec approved]
  S --> B[Built and<br/>unit tested]
  B --> T[String, end-to-end,<br/>volume tested]
  T --> C[Cut over<br/>with rollback]
  C --> H[Hypercare]
  H --> R[Run:<br/>monitored, owned]

Each column of the inventory answers one question an architecture board, a test manager or a cutover manager will ask. The integration lead's job is to keep every row moving right, and to know at any moment which rows are stuck and why.

The second idea: every failure has an owner before it happens. An interface is not designed until someone has said, for each kind of error, where the message stops, who is told, who fixes it and how it is run again. Most go-live incidents in integration are not technical failures; they are errors nobody was assigned to.

How it works

The interface inventory

The inventory is one table, one row per interface, kept in one place (a sheet, SAP Cloud ALM's requirements, or a file in the project repository). SAP's private edition training starts it from the Digital Discovery Assessment in the Discover phase, grows it in fit-to-standard workshops and records the integration requirements in Cloud ALM's Requirements app (SAP Learning). The columns that earn their place:

Column group Columns Who asks for it
Identity ID, name, sender, receiver Everyone
Design Pattern, technology (the released API, event, IDoc or flow) Architects
Load Volume per day, peak per hour, maximum delay Non-functional design, volume test
Ownership Business owner, technical owner, error owner Operations, hypercare
Operations How to reprocess, where it is monitored, who gets alerts Support
Status Spec status, test status Test manager, steering committee
Cutover Action at go-live (unchanged, extend, retire, new), rollback Cutover manager

Two rules keep it honest. Status columns use fixed values, so a script can count them. And the inventory is the source, so specs, test plans and cutover steps are generated from it or checked against it, never typed separately.

Specs and mapping documents

A spec has a functional half (purpose, trigger, scope, business rules, errors) and a technical half (technology, mapping, security, retries, monitoring). On a small program one document holds both. The parts that cause the most trouble when missing:

  • Out of scope. Order changes and cancellations are usually a separate interface. Saying so stops a tester from failing the order interface because a change didn't arrive.
  • The mapping table. One row per target field: source field, target field, rule, and what happens when the source is wrong. "Copy" is a rule; "map" is not.
  • The error table. Each error with a code, where the message stops, who owns it and what happens next.
  • Test cases with concrete data, so the spec and the test plan can't drift apart.

For business-to-business (B2B) messages, SAP's Integration Advisor keeps the message and mapping side of the spec as data: a message implementation guideline (MIG) describes one partner's version of a message, and a mapping guideline (MAG) maps a source MIG to a target MIG. SAP's product team described in 2022 how the tool generates runtime artifacts and test examples from a MAG and exports MIGs and MAGs as PDF documents (SAP Technology Blog). The mapping document and the running mapping then come from the same source.

Test strategy

SAP Activate names the levels: unit tests in the Realize sprints, string tests at times, a full integration test and user acceptance before Deploy, and performance testing in the testing workstream (SAP Learning). For interfaces, the levels mean:

Level Proves Typical interface cases Who runs it
Unit One mapping or rule works Each mapping row with a good and a bad value Developer
String A chain of steps works across systems Partner to platform to S/4HANA and back; receiver down; same message twice Integration team
End-to-end (integration test) The business process works with the interface inside it Distributor order to delivery to invoice; an error fixed and reprocessed Process teams
Volume It holds the service level at load Twice the peak hour; the month-end batch Integration and basis teams

Two cases belong in every string test: the receiver is down (do retries work, and does the message arrive once?) and the same message twice (does a duplicate create a second order?). These are the failures you simulated in the Unit 5B setup mock and studied in how SAP systems talk.

Error handling: who reprocesses what

Sort errors into two kinds before assigning them:

  • Technical errors: connection timeouts, expired credentials, a broken mapping, a full queue. The technical owner fixes them; the message usually runs again unchanged.
  • Business errors: unknown customer, unknown part, duplicate purchase order number, missing price. Only the business can fix them correctly, usually in master data; then the message runs again.

The design question is where the message waits and what the business owner can see. Integration Suite's B2B monitoring shows the interchanges of each partner and links to the message processing logs (SAP Technology Blog, 2021). Inside S/4HANA, failed inbound IDocs and AIF messages are monitored where they stopped; Cloud ALM's content list covers both for S/4HANA on premise and private cloud (SAP Support). SAP Application Interface Framework adds a business-facing view with correction and mass error handling, with sensitive fields hidden by role (SAP). Whatever the tool, record in the spec: who may change a message's data, and how that change is logged.

Monitoring and alerting

SAP Cloud ALM's Integration & Exception Monitoring correlates messages across components, groups them into scenarios, searches single messages by business context such as an order number (if the sender passes it), and sends alerts to an Alert Inbox where someone assigns a processor, comments, postpones or confirms (SAP Support). Coverage differs by product: the content list names IDocs and AIF messages for SAP S/4HANA on premise and private cloud, and single message monitoring for Cloud Integration (SAP Support). Put the monitoring tool and the alert receiver in the inventory per interface; an interface with "none" is found by a customer.

Cutover and data migration sequencing

Interfaces sit inside the data migration cutover, not beside it. Calder's plan in the data migration topic freezes master data in ECC on Monday 28 June 2027, stops postings and EDI to CP10 on Wednesday evening, loads from Friday, decides go/no-go on Sunday at 12:00 and keeps ECC able to reopen until 18:00. For a company code rollout into a live system, most of CP10's interfaces never stop. Each interface gets one of four actions:

Action Meaning Calder Rule
unchanged Not affected I01-I06, I09, I10, I12 Watched in hypercare anyway
extend Same interface, new company code or plant I07 data warehouse, I08 bank Configuration transported switched off; switched on after go/no-go
retire The interface disappears I11 intercompany EDI Drained before the load; deactivated only after the fallback deadline
new First go-live none yet (the exercise adds one) Switched on after go/no-go; first message checked by its owner

The ordering rules: stop inbound before the last postings, so nothing arrives in a frozen system; drain queues and error lists before extraction, so the open items you migrate are complete; switch on after go/no-go, never before; and retire after the fallback deadline, so the way back still works.

Hypercare

Hypercare is part of SAP Activate's Deploy phase (SAP Learning). For interfaces it means a daily error review per owner, a short list of known issues, and an exit rule agreed before go-live (for example, three days with no new kind of error on any interface). Errors repeat in families: the same unknown part number from the same distributor ten times is one problem, not ten.

Where AI speeds the work, and the controls

Task What AI does well Control that makes it safe
Drafting specs First draft from the inventory row, workshop notes and the API's documentation Owners review and sign; the draft is marked as a draft
Mapping proposals SAP's Integration Advisor offers AI-powered mapping proposals (SAP) A developer accepts each field; unit tests per mapping row
Test cases Lists cases from the mapping and error tables Tester adds data and expected results; coverage checked against the spec
Long documents SAP Cloud ALM's AI-assisted document summary is generally available as of Q2 2026 (SAP News) Summary edited and saved by a person
Error triage Groups similar errors, suggests likely causes Deterministic grouping first; numbers masked before any text leaves; an approved model only; no automatic reprocessing

The same SAP release summary reports a customer seeing faster unit testing with Joule generating test cases; that is one customer's report, not a benchmark (SAP News). The control that matters most: an AI never reprocesses or edits a message. It proposes; the error owner acts. Agents as integration consumers are covered in Unit 9's tool and MCP topics.

Build it yourself: Calder's interface inventory and the I10 spec

You will build the integration lead's working files for Calder: an interface inventory as a CSV file, a checker that finds missing owners and missing rollbacks, a complete spec for interface I10 (distributor purchase orders), the interface steps of the July 2027 cutover, and a first-pass triage of an error log, with a masked prompt you could give an approved AI model. One script does all of it.

flowchart LR
  T[template] --> INV[(calder_interfaces.csv)]
  INV --> CHK[check]
  INV --> SPEC[spec I10<br/>spec_I10.md]
  INV --> CUT[cutover]
  LOG[error log] --> TRI[triage]
  INV --> TRI
  TRI --> P[triage_prompt.txt<br/>masked]

Before you start: complete Set up your computer for this course and Set up for Unit 5B. You need the orchestrate-course folder with its .venv and a unit5b folder. This topic installs no libraries and adds nothing to requirements.txt.

What you need

  • Your course folder and the unit5b folder.
  • Python 3.10 or later. No accounts, no internet, no cost.
  • About 40 minutes.

All interfaces, owners, volumes, statuses, dates and rules are fictional Calder course values. Interface IDs I01 to I12 come from how SAP systems talk and the integration platform and clean core; the cutover milestones come from the data migration topic; the test messages HFS-PO-7781, HFS-PO-7790 and XYZ-PO-0001 are the ones from the Unit 5B setup mock.

Step 1: Open the course folder and turn on the virtual environment

  1. Open VS Code, choose File > Open Folder and open orchestrate-course.
  2. Open a terminal with Terminal > New Terminal.
  3. Turn on the virtual environment if the prompt doesn't start with (.venv):
    • Windows (PowerShell):

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

      source .venv/bin/activate

Step 2: Create the script

  1. Create a new file, paste the script below, and save it as integration_toolkit.py inside unit5b.
"""The integration lead's toolkit for Calder Pumpworks (Unit 5B).

Calder Pumpworks is a fictional company. Interface IDs reuse the earlier
Unit 5B topics. Owners, volumes, statuses, dates and rules are course values,
written to teach the method, not SAP figures.

  python unit5b/integration_toolkit.py template           write unit5b/calder_interfaces.csv
  python unit5b/integration_toolkit.py check              check the inventory
  python unit5b/integration_toolkit.py spec I10           write unit5b/spec_I10.md
  python unit5b/integration_toolkit.py cutover            interface steps for the July 2027 rollout
  python unit5b/integration_toolkit.py triage --sample    group a sample error log by cause
  python unit5b/integration_toolkit.py triage --sample --prompt   also write a masked prompt file

Only Python's standard library is used. Nothing goes over the internet.
"""
import argparse
import csv
import re
import sys
from collections import OrderedDict
from pathlib import Path

FOLDER = Path("unit5b")
INVENTORY = FOLDER / "calder_interfaces.csv"

COLUMNS = ["id", "name", "sender", "receiver", "pattern", "technology",
           "volume_per_day", "peak_per_hour", "max_delay",
           "business_owner", "technical_owner", "error_owner", "reprocess",
           "monitoring", "alert_to", "spec_status", "test_status",
           "cutover", "rollback"]

PATTERNS = {"sync-api", "async-message", "event", "file-batch", "b2b-edi", "extraction"}
SPEC_STATUS = {"draft", "approved", "standard"}      # standard = SAP-delivered, no own spec
TEST_STATUS = ["not started", "unit", "string", "end-to-end", "volume", "passed"]
CUTOVER = {"unchanged", "extend", "retire", "new"}
DELAY_S = {"2s": 2, "5min": 300, "15min": 900, "1h": 3600, "4h": 14400, "1d": 86400}

# The target design from ADR-001 (previous topic), as an inventory. Four rows
# have deliberate gaps for you to find with "check" and fix in Step 4.
TEMPLATE = [
    ["I01", "Purchase orders and confirmations", "S/4HANA CP10", "SAP Business Network",
     "async-message", "SAP-delivered network integration", "120", "30", "1h",
     "Purchasing", "Procurement IT", "Purchasing", "Fix data; resend from the sender",
     "Cloud ALM", "Procurement IT", "standard", "passed", "unchanged", ""],
    ["I02", "Released production orders", "S/4HANA CP10", "MES Rockford",
     "async-message", "Released API via Integration Suite and Cloud Connector", "300", "60", "5min",
     "Manufacturing", "Manufacturing IT", "Production planning", "Correct the order; resend from Integration Suite",
     "Cloud ALM", "Manufacturing IT", "approved", "string", "unchanged", ""],
    ["I03", "Sales order status", "S/4HANA CP10", "CRM",
     "event", "Sales order events; CRM reads the order by released API", "450", "90", "15min",
     "Sales", "Sales IT", "Sales IT", "Replay the event; CRM reads the current order",
     "Cloud ALM", "Sales IT", "draft", "passed", "unchanged", ""],
    ["I04", "Changed order confirmations", "S/4HANA CP10", "Customers",
     "async-message", "Standard output management; email PDF", "300", "60", "1h",
     "Customer service", "Sales IT", "Customer service", "Send the output again from the order",
     "Output status in S/4HANA", "Customer service", "standard", "passed", "unchanged", ""],
    ["I05", "Deliveries and goods issue", "S/4HANA CP10", "EWM RKDC",
     "async-message", "Standard delivery replication (queued)", "280", "60", "15min",
     "Logistics", "Logistics IT", "Warehouse supervisors", "Fix the cause; restart the queue",
     "Cloud ALM", "Logistics IT", "standard", "passed", "unchanged", ""],
    ["I06", "Freight orders and carrier invoices", "S/4HANA CP10", "Carriers",
     "b2b-edi", "Trading Partner Management B2B flows", "40", "10", "1h",
     "Transport planning", "Integration team", "", "Fix partner data; reprocess in B2B monitoring",
     "Integration Suite B2B monitoring", "Transport planning", "approved", "string", "unchanged", ""],
    ["I07", "Open orders and billing", "S/4HANA CP10", "Data warehouse",
     "extraction", "CDS extraction to Datasphere", "15000", "15000", "1d",
     "Controlling", "Analytics team", "Analytics team", "Rerun the load",
     "Datasphere monitoring", "Analytics team", "approved", "end-to-end", "extend",
     "Keep the CP20 SLT route until the fallback deadline"],
    ["I08", "Bank statements", "Bank", "S/4HANA CP10",
     "file-batch", "SAP Multi-Bank Connectivity", "2", "2", "1d",
     "Treasury", "Finance IT", "Treasury", "Ask the bank to send the statement again",
     "Cloud ALM", "Treasury", "approved", "end-to-end", "extend",
     "Augsburg statements stay on ECC until the fallback deadline"],
    ["I09", "Exceptions for the supply agent", "S/4HANA CP10", "Agent on BTP",
     "event", "Business events plus released read APIs", "200", "40", "15min",
     "Supply chain", "Integration lead", "Integration lead", "Replay the event; the agent reads current state",
     "Cloud ALM", "Integration lead", "draft", "unit", "unchanged", ""],
    ["I10", "Distributor purchase orders", "Distributors", "S/4HANA CP10",
     "b2b-edi", "Trading Partner Management to IDoc ORDERS", "80", "30", "1h",
     "Customer service", "Integration team", "Customer service", "Correct the cause; reprocess the message",
     "Integration Suite B2B monitoring; Cloud ALM", "Customer service", "approved", "end-to-end", "unchanged", ""],
    ["I11", "Intercompany orders and invoices", "S/4HANA CP10", "ECC CP20",
     "b2b-edi", "IDoc-based EDI between the systems", "20", "8", "4h",
     "Intercompany team", "Integration team", "Intercompany team", "Reprocess the IDoc in the receiving system",
     "IDoc monitoring in each system", "Intercompany team", "approved", "passed", "retire", ""],
    ["I12", "Availability check while quoting", "CRM", "S/4HANA CP10",
     "sync-api", "Released API behind API Management", "60", "20", "2s",
     "Sales", "Sales IT", "Sales IT", "Caller retries; the user sees an error",
     "none", "Sales IT", "approved", "string", "unchanged", ""],
]


def load(path):
    if not path.exists():
        sys.exit(f"{path} not found. Run: python unit5b/integration_toolkit.py template")
    with path.open(newline="", encoding="utf-8") as f:
        rows = list(csv.DictReader(f))
    missing = [c for c in COLUMNS if rows and c not in rows[0]]
    if missing:
        sys.exit(f"{path} is missing columns: {', '.join(missing)}")
    return rows


def cmd_template(args):
    FOLDER.mkdir(exist_ok=True)
    if INVENTORY.exists() and not args.force:
        sys.exit(f"{INVENTORY} exists. Add --force to overwrite it.")
    with INVENTORY.open("w", newline="", encoding="utf-8") as f:
        w = csv.writer(f)
        w.writerow(COLUMNS)
        w.writerows(TEMPLATE)
    print(f"Wrote {INVENTORY} with {len(TEMPLATE)} interfaces and {len(COLUMNS)} columns.")


def check_row(r):
    """Return a list of (level, message) for one inventory row."""
    out = []
    for col in COLUMNS:
        if col != "rollback" and not r[col].strip():
            out.append(("FAIL", f"{col} is empty"))
    if r["pattern"] and r["pattern"] not in PATTERNS:
        out.append(("FAIL", f"unknown pattern '{r['pattern']}'"))
    if r["max_delay"] and r["max_delay"] not in DELAY_S:
        out.append(("FAIL", f"max_delay must be one of {', '.join(DELAY_S)}"))
    try:
        per_day, peak = int(r["volume_per_day"]), int(r["peak_per_hour"])
        if peak * 24 < per_day:
            out.append(("FAIL", "peak_per_hour x 24 is below volume_per_day"))
    except ValueError:
        out.append(("FAIL", "volume_per_day and peak_per_hour must be whole numbers"))
    if r["monitoring"].strip().lower() == "none":
        out.append(("FAIL", "no monitoring: a failure here is found by a customer"))
    if r["pattern"] in ("b2b-edi", "async-message") and r["error_owner"] \
            and r["error_owner"] == r["technical_owner"]:
        out.append(("WARN", "error owner is the technical team; business errors need a business owner"))
    if r["pattern"] == "sync-api" and DELAY_S.get(r["max_delay"], 0) > 60:
        out.append(("WARN", "synchronous, but nobody waits: consider async"))
    if r["spec_status"] and r["spec_status"] not in SPEC_STATUS:
        out.append(("FAIL", f"spec_status must be one of {', '.join(sorted(SPEC_STATUS))}"))
    if r["test_status"] and r["test_status"] not in TEST_STATUS:
        out.append(("FAIL", f"test_status must be one of {', '.join(TEST_STATUS)}"))
    if r["spec_status"] == "draft" and r["test_status"] == "passed":
        out.append(("WARN", "tests passed against a spec nobody approved"))
    if r["cutover"] and r["cutover"] not in CUTOVER:
        out.append(("FAIL", f"cutover must be one of {', '.join(sorted(CUTOVER))}"))
    if r["cutover"] in ("extend", "retire", "new") and not r["rollback"].strip():
        out.append(("FAIL", f"cutover '{r['cutover']}' without a rollback step"))
    return out


def cmd_check(args):
    rows = load(Path(args.file))
    fails = warns = 0
    for r in rows:
        for level, msg in check_row(r):
            print(f"  {level:5} {r['id']:4} {msg}")
            fails += level == "FAIL"
            warns += level == "WARN"
    clean = sum(1 for r in rows if not check_row(r))
    print(f"\n{len(rows)} interfaces: {clean} clean, {fails} FAIL, {warns} WARN")
    by_test = OrderedDict((s, 0) for s in TEST_STATUS)
    for r in rows:
        if r["test_status"] in by_test:
            by_test[r["test_status"]] += 1
    print("Test status: " + ", ".join(f"{k} {v}" for k, v in by_test.items() if v))
    sys.exit(1 if fails else 0)


# Detail for the one complete spec in this topic. Course mapping and rules.
I10_DETAIL = {
    "purpose": "Distributors such as Hallam Fluid Systems send purchase orders by EDI. "
               "Each one becomes a sales order in CP10 without retyping, within an hour.",
    "trigger": "The distributor sends a purchase order to Calder's EDI address. "
               "One message holds one purchase order with one or more lines.",
    "out_of_scope": "Order changes and cancellations (separate interface, to be specified); "
                    "the order confirmation back to the distributor (I04 today).",
    "mapping": [
        ("Sender identifier", "Sold-to party", "Look up in the trading partner profile: Hallam = 10000002; unknown sender -> E01"),
        ("Ship-to location code", "Ship-to party", "Cross-reference kept by sales master data; blank -> same as sold-to"),
        ("Purchase order number", "Customer reference (customer PO)", "Copy; same number for the same sold-to already exists -> E03"),
        ("Purchase order date", "Customer reference date", "Copy as a date (YYYY-MM-DD)"),
        ("Line number", "Item number", "Line x 10 (1 -> 10, 2 -> 20)"),
        ("Distributor part number", "Product", "Cross-reference: HFS-SK-200 -> CP-SK200; unknown -> E02"),
        ("Quantity and unit", "Order quantity and unit", "Course code list: EA or PCE -> PC; unknown unit -> E04"),
        ("Requested date", "Requested delivery date", "Copy; a date in the past is kept and flagged for customer service"),
        ("Unit price", "(not mapped)", "S/4HANA determines the price; a price dispute is a sales issue, not an interface error"),
        ("(constant)", "Sales area", "Sales org CPUS, distribution channel 20, division PU (from the partner agreement)"),
    ],
    "errors": [
        ("E01", "Unknown sender", "Integration Suite (partner not found)", "Integration team",
         "Not reprocessed: onboard the partner, then the distributor resends"),
        ("E02", "Unknown part number", "S/4HANA inbound (message in error)", "Customer service",
         "Add the cross-reference; reprocess the same message"),
        ("E03", "Duplicate customer PO", "S/4HANA inbound (no second order)", "Customer service",
         "Confirm with the distributor; cancel the duplicate message"),
        ("E04", "Unknown unit of measure", "Integration Suite (mapping)", "Integration team",
         "Extend the code list; reprocess from the B2B monitor"),
        ("E05", "S/4HANA not reachable", "Integration Suite (delivery)", "Integration team",
         "Automatic retry (4 tries, backoff 0.5 s doubling to 4 s); then alert and reprocess"),
        ("E06", "Order created but blocked (credit)", "S/4HANA (not an interface error)", "Credit management",
         "Normal order-to-cash worklist; the interface did its job"),
    ],
    "security": [
        "Distributors are identified by the trading partner profile, never by a value they type.",
        "A technical user posts into CP10 with a role limited to creating sales orders in sales org CPUS.",
        "Messages hold customer names and addresses: keep payloads in monitoring only as long as the retention rule says.",
        "Nobody edits a payload to make it pass without a record of who changed what.",
    ],
    "tests": [
        ("unit", "Each mapping row with a valid and an invalid value", "Rows 1-10; E02 and E04 values"),
        ("string", "Message through Integration Suite into CP10, acknowledgment back", "HFS-PO-7781: 60 CP-SK200 -> one sales order"),
        ("string", "S/4HANA briefly down", "HFS-PO-7790 while CP10 is unavailable -> retries, then one order"),
        ("string", "Same message sent twice", "HFS-PO-7781 again -> E03, still one order"),
        ("string", "Unknown sender", "XYZ-PO-0001 -> E01, rejected, alert to the integration team"),
        ("end-to-end", "Distributor order to invoice", "HFS-PO-7781 -> order -> delivery from RKDC -> invoice -> I04 confirmation"),
        ("end-to-end", "Error, fix, reprocess", "Unknown part number -> E02 -> customer service adds it -> reprocess -> order"),
        ("volume", "Twice the peak hour", "60 messages in one hour; all orders created within max delay"),
    ],
}


def spec_text(r, detail):
    d = detail or {}
    todo = "_To be written._"
    lines = [f"# Interface specification {r['id']}: {r['name']}", "",
             "Calder Pumpworks (fictional). Course template; values are course values.", "",
             "| Field | Value |", "| --- | --- |"]
    for label, col in [("Sender", "sender"), ("Receiver", "receiver"), ("Pattern", "pattern"),
                       ("Technology", "technology"), ("Business owner", "business_owner"),
                       ("Technical owner", "technical_owner"), ("Error owner", "error_owner"),
                       ("Spec status", "spec_status")]:
        lines.append(f"| {label} | {r[col]} |")
    lines += ["", "## 1. Business purpose", "", d.get("purpose", todo), "",
              "## 2. Trigger and scope", "", d.get("trigger", todo), "",
              f"Out of scope: {d.get('out_of_scope', todo)}", "",
              "## 3. Volumes and service levels", "",
              "| Measure | Value |", "| --- | --- |",
              f"| Messages per day | {r['volume_per_day']} |",
              f"| Peak per hour | {r['peak_per_hour']} |",
              f"| Maximum delay before the business notices | {r['max_delay']} |",
              "", "## 4. Mapping", ""]
    if d.get("mapping"):
        lines += ["| # | Source | Target | Rule |", "| --- | --- | --- | --- |"]
        lines += [f"| {i} | {s} | {t} | {rule} |" for i, (s, t, rule) in enumerate(d["mapping"], 1)]
    else:
        lines.append(todo)
    lines += ["", "## 5. Errors and reprocessing", ""]
    if d.get("errors"):
        lines += ["| Code | Error | Where it stops | Owner | What happens next |",
                  "| --- | --- | --- | --- | --- |"]
        lines += [f"| {c} | {e} | {w} | {o} | {n} |" for c, e, w, o, n in d["errors"]]
    else:
        lines.append(todo)
    lines += ["", f"Reprocessing: {r['reprocess']}.", "",
              "## 6. Monitoring and alerting", "",
              f"Monitored in: {r['monitoring']}. Alerts go to: {r['alert_to']}.", "",
              "## 7. Security", ""]
    lines += [f"- {s}" for s in d.get("security", [])] or [todo]
    lines += ["", "## 8. Test cases", ""]
    if d.get("tests"):
        lines += ["| # | Level | Case | Data and expected result |", "| --- | --- | --- | --- |"]
        lines += [f"| T{i:02} | {lvl} | {c} | {x} |" for i, (lvl, c, x) in enumerate(d["tests"], 1)]
    else:
        lines.append(todo)
    lines += ["", "## 9. Cutover and hypercare", "",
              f"Cutover action: {r['cutover']}. Rollback: {r['rollback'] or 'none needed'}.", "",
              "Hypercare: the error owner reviews every error of this interface daily for the first two weeks.",
              "", "## 10. Sign-off", "",
              "| Role | Name | Date |", "| --- | --- | --- |",
              "| Business owner | | |", "| Technical owner | | |", "| Error owner | | |", ""]
    return "\n".join(lines)


def cmd_spec(args):
    rows = {r["id"]: r for r in load(Path(args.file))}
    iid = args.id.upper()
    if iid not in rows:
        sys.exit(f"{iid} is not in the inventory. Known: {', '.join(rows)}")
    detail = I10_DETAIL if iid == "I10" else None
    text = spec_text(rows[iid], detail)
    out = FOLDER / f"spec_{iid}.md"
    out.write_text(text, encoding="utf-8")
    gaps = text.count("_To be written._")
    print(f"Wrote {out} ({len(text.splitlines())} lines, {gaps} sections still to write)")


# Milestones from the data migration topic's cutover plan (course dates, CET).
MILESTONES = [
    ("2027-06-28 08:00", "freeze", "Master data freeze in ECC CP20"),
    ("2027-06-30 18:00", "last_post", "Last postings in ECC CP20"),
    ("2027-07-02 18:00", "load", "Extract and load start"),
    ("2027-07-04 12:00", "go_no_go", "Go/no-go decision"),
    ("2027-07-04 18:00", "fallback", "Fallback deadline"),
    ("2027-07-05 08:00", "go_live", "Augsburg works in S/4HANA"),
]
WHEN = {key: t for t, key, _ in MILESTONES}

# What each cutover action means, as (milestone, offset label, step).
STEPS = {
    "retire": [("last_post", "at", "Stop new messages from CP10; nothing new enters the queues"),
               ("load", "before", "Every message processed or cancelled; error list empty and signed"),
               ("fallback", "after", "Deactivate the partner settings in both systems; keep logs")],
    "extend": [("load", "before", "Configuration for CP20 transported with the release, switched off"),
               ("go_no_go", "after", "Switch on for CP20"),
               ("go_live", "at", "First CP20 message checked by the error owner")],
    "new": [("go_no_go", "after", "Activate the new interface"),
            ("go_live", "at", "First message checked by the error owner")],
}


def cmd_cutover(args):
    rows = [r for r in load(Path(args.file)) if r["cutover"] in STEPS]
    plan = []
    for r in rows:
        for key, rel, text in STEPS[r["cutover"]]:
            plan.append((WHEN[key], rel, key, r["id"], r["error_owner"] or "(no owner)", text))
        if r["rollback"].strip():
            plan.append((WHEN["fallback"], "until", "fallback", r["id"], r["error_owner"],
                         f"Rollback ready: {r['rollback']}"))
    print("Interface steps in the cutover plan (course dates, CET)\n")
    order = {"before": 0, "at": 1, "until": 2, "after": 3}
    for t, label, _ in MILESTONES:
        print(f"{t}  -- {label} --")
        for when, rel, key, iid, owner, text in sorted(plan, key=lambda p: (p[3], order[p[1]])):
            if when == t:
                print(f"    {rel:6} {iid}  {text}  [{owner}]")
    blocked = [r["id"] for r in rows if not r["rollback"].strip()]
    if blocked:
        print(f"\nBLOCKED: no rollback for {', '.join(blocked)}. A go/no-go needs a way back.")
        sys.exit(1)
    print(f"\n{len(rows)} interfaces change at cutover; each has a rollback step.")


SAMPLE_LOG = """\
2027-07-05T07:12:03 I10 ERROR HFS-PO-8102 product for distributor part HFS-SK-210 not found
2027-07-05T07:40:11 I10 ERROR HFS-PO-8105 product for distributor part HFS-SK-210 not found
2027-07-05T08:02:45 I06 ERROR LFL-FO-6100000031 carrier 20000005 rejected tender: unknown location code AU01
2027-07-05T08:15:09 I10 ERROR HFS-PO-8109 unit of measure BX not in code list
2027-07-05T08:31:52 I02 ERROR order 1100044 delivery to MES failed: connection timed out after 30 s
2027-07-05T08:31:58 I02 ERROR order 1100045 delivery to MES failed: connection timed out after 30 s
2027-07-05T08:32:04 I02 ERROR order 1100046 delivery to MES failed: connection timed out after 30 s
2027-07-05T09:05:30 I10 ERROR HFS-PO-8102 customer reference already used by sold-to 10000002
2027-07-05T09:47:13 I10 ERROR HFS-PO-8117 product for distributor part HFS-SK-210 not found
2027-07-05T10:20:41 I03 ERROR event for order 1000311 rejected by CRM: 401 unauthorized
2027-07-05T10:21:02 I03 ERROR event for order 1000312 rejected by CRM: 401 unauthorized
2027-07-05T11:02:18 I08 ERROR statement for account ending 4410 not assigned to a house bank
"""

CATEGORIES = [  # first match wins; course rules
    ("master data", r"not found|unknown|not assigned"),
    ("mapping", r"code list|conversion"),
    ("duplicate", r"already used|duplicate"),
    ("connectivity", r"timed out|connection|503|unreachable"),
    ("authorization", r"401|403|unauthori[sz]ed|forbidden"),
]
TECHNICAL = {"connectivity", "authorization", "mapping"}   # to the technical owner
NEXT_STEP = {
    "master data": "fix the master data or cross-reference, then reprocess",
    "mapping": "extend the mapping or code list, then reprocess",
    "duplicate": "confirm with the sender; cancel the duplicate",
    "connectivity": "check the receiver and connection; reprocess after recovery",
    "authorization": "check the technical user's credentials and roles",
    "unknown": "read the full message log",
}


def signature(text):
    """Replace document numbers and IDs so similar errors group together."""
    text = re.sub(r"\b[A-Z]{2,4}-[A-Z]{2}-\d+\b", "<doc>", text)
    text = re.sub(r"\b\d+\b", "<n>", text)
    return text


def mask(line):
    """Mask partner and document numbers before text leaves the company.
    The timestamp (first word) is kept."""
    ts, _, rest = line.partition(" ")
    rest = re.sub(r"\b[A-Z]{2,4}-[A-Z]{2}-\d+\b", "<DOC>", rest)
    return ts + " " + re.sub(r"\b\d{4,}\b", "<NUM>", rest)


def cmd_triage(args):
    if args.sample:
        text = SAMPLE_LOG
    elif args.log:
        text = Path(args.log).read_text(encoding="utf-8")
    else:
        sys.exit("Give a log file or --sample")
    owners = {}
    if Path(args.file).exists():
        owners = {r["id"]: r for r in load(Path(args.file))}
    groups = OrderedDict()
    for line in text.strip().splitlines():
        parts = line.split(" ", 3)
        if len(parts) < 4 or parts[2] != "ERROR":
            continue
        ts, iid, _lvl, msg = parts
        cat = next((c for c, pat in CATEGORIES if re.search(pat, msg, re.I)), "unknown")
        key = (iid, signature(msg))
        g = groups.setdefault(key, {"count": 0, "first": ts, "last": ts, "cat": cat, "example": msg})
        g["count"] += 1
        g["last"] = ts
    print(f"{len(groups)} distinct problems in {sum(g['count'] for g in groups.values())} errors\n")
    ranked = sorted(groups.items(), key=lambda kv: -kv[1]["count"])
    for (iid, sig), g in ranked:
        row = owners.get(iid, {})
        col = "technical_owner" if g["cat"] in TECHNICAL else "error_owner"
        owner = row.get(col) or "(no owner in inventory)"
        print(f"{g['count']:3}x {iid} {g['cat']:13} owner: {owner}")
        print(f"      {sig}")
        print(f"      next: {NEXT_STEP[g['cat']]}")
    if args.prompt:
        out = FOLDER / "triage_prompt.txt"
        FOLDER.mkdir(exist_ok=True)
        body = "\n".join(mask(l) for l in text.strip().splitlines())
        out.write_text(
            "You help an SAP integration team triage interface errors. For each group of\n"
            "similar errors, suggest the likely cause and the first check. Do not invent\n"
            "document numbers. Say 'unknown' when the log doesn't show the cause.\n\n"
            f"Log (numbers masked):\n{body}\n", encoding="utf-8")
        print(f"\nWrote {out}: numbers masked. Paste it only into a model your company approved.")


def main():
    p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    p.add_argument("--file", default=str(INVENTORY), help="inventory CSV")
    sub = p.add_subparsers(dest="cmd", required=True)
    t = sub.add_parser("template")
    t.add_argument("--force", action="store_true")
    t.set_defaults(fn=cmd_template)
    sub.add_parser("check").set_defaults(fn=cmd_check)
    s = sub.add_parser("spec")
    s.add_argument("id")
    s.set_defaults(fn=cmd_spec)
    sub.add_parser("cutover").set_defaults(fn=cmd_cutover)
    tr = sub.add_parser("triage")
    tr.add_argument("log", nargs="?")
    tr.add_argument("--sample", action="store_true")
    tr.add_argument("--prompt", action="store_true")
    tr.set_defaults(fn=cmd_triage)
    a = p.parse_args()
    a.fn(a)


if __name__ == "__main__":
    main()

Step 3: Write the inventory and check it

  1. Write the inventory file:

    python unit5b/integration_toolkit.py template
    Wrote unit5b/calder_interfaces.csv with 12 interfaces and 19 columns.
  2. Open unit5b/calder_interfaces.csv in VS Code to see the 19 columns. Values never contain commas (they use semicolons), so the file stays easy to edit by hand.

  3. Check it:

    python unit5b/integration_toolkit.py check
      WARN  I03  tests passed against a spec nobody approved
      FAIL  I06  error_owner is empty
      FAIL  I11  cutover 'retire' without a rollback step
      FAIL  I12  no monitoring: a failure here is found by a customer
    
    12 interfaces: 8 clean, 3 FAIL, 1 WARN
    Test status: unit 1, string 3, end-to-end 3, passed 5

The four findings are deliberate. Each is a classic gap: an interface with no one to fix business errors (carriers), a retirement with no way back (intercompany EDI), a synchronous call nobody watches (the CRM's availability check), and test results against a draft spec. A clean run ends with exit code 0, a run with any FAIL with exit code 1, so the check can later run in a pipeline.

Step 4: Fix the gaps

Edit the CSV in VS Code. Change only the cells named here, keep the commas between columns, and save.

  1. Row I06: the 12th column (error_owner) is empty, shown as two commas next to each other after Integration team. Type Transport planning between them.

  2. Row I11: the last column (rollback) is empty; the line ends with retire,. Add after the comma: Keep the partner settings; restart CP10 sending if no-go

  3. Row I12: replace none with API Management analytics; Cloud ALM

  4. Row I03: replace passed with string. Tests run against a draft don't count until the spec is approved; record the level you can defend.

  5. Run the check again:

    python unit5b/integration_toolkit.py check
    12 interfaces: 12 clean, 0 FAIL, 0 WARN
    Test status: unit 1, string 4, end-to-end 3, passed 4

If you want to start over, run python unit5b/integration_toolkit.py template --force.

Step 5: Generate the I10 spec

  1. Run:

    python unit5b/integration_toolkit.py spec I10
    Wrote unit5b/spec_I10.md (98 lines, 0 sections still to write)
  2. Open unit5b/spec_I10.md and choose Open Preview (the icon at the top right of the editor, or Ctrl+Shift+V on Windows, Cmd+Shift+V on macOS). You see ten sections: purpose, trigger and scope, volumes, a ten-row mapping, six error cases with owners, monitoring, security, eight test cases across four levels, cutover and sign-off.

  3. Read the error table. Check that every error has an owner and a next step, and that E06 (a credit block) is marked as not an interface error. Mixing those up is how a credit hold becomes an integration incident at 2 a.m.

  4. Try another interface to see a skeleton:

    python unit5b/integration_toolkit.py spec I03
    Wrote unit5b/spec_I03.md (68 lines, 7 sections still to write)

The header, volumes, monitoring and cutover sections come from the inventory row, so they never disagree with it. The business sections say _To be written._ until someone writes them.

Step 6: Print the interface steps of the cutover

  1. Run:

    python unit5b/integration_toolkit.py cutover
    Interface steps in the cutover plan (course dates, CET)
    
    2027-06-28 08:00  -- freeze --
    2027-06-30 18:00  -- last_post --
        at     I11  Stop new messages from CP10; nothing new enters the queues  [Intercompany team]
    2027-07-02 18:00  -- load --
        before I07  Configuration for CP20 transported with the release, switched off  [Analytics team]
        before I08  Configuration for CP20 transported with the release, switched off  [Treasury]
        before I11  Every message processed or cancelled; error list empty and signed  [Intercompany team]
    2027-07-04 12:00  -- go_no_go --
        after  I07  Switch on for CP20  [Analytics team]
        after  I08  Switch on for CP20  [Treasury]
    2027-07-04 18:00  -- fallback --
        until  I07  Rollback ready: Keep the CP20 SLT route until the fallback deadline  [Analytics team]
        until  I08  Rollback ready: Augsburg statements stay on ECC until the fallback deadline  [Treasury]
        until  I11  Rollback ready: Keep the partner settings; restart CP10 sending if no-go  [Intercompany team]
        after  I11  Deactivate the partner settings in both systems; keep logs  [Intercompany team]
    2027-07-05 08:00  -- go_live --
        at     I07  First CP20 message checked by the error owner  [Analytics team]
        at     I08  First CP20 message checked by the error owner  [Treasury]
    
    3 interfaces change at cutover; each has a rollback step.
  2. Read it against the ordering rules: I11 stops before the last postings, is drained before the load, and is deactivated only after the fallback deadline. The extended interfaces switch on only after go/no-go.

  3. If you skipped the I11 fix in Step 4, the output ends with BLOCKED: no rollback for I11 and exit code 1. A go/no-go needs a way back.

Step 7: Triage an error log

  1. Run the triage on the built-in sample, a made-up log from the first morning of hypercare:

    python unit5b/integration_toolkit.py triage --sample --prompt
    7 distinct problems in 12 errors
    
      3x I10 master data   owner: Customer service
          <doc> product for distributor part <doc> not found
          next: fix the master data or cross-reference, then reprocess
      3x I02 connectivity  owner: Manufacturing IT
          order <n> delivery to MES failed: connection timed out after <n> s
          next: check the receiver and connection; reprocess after recovery
      2x I03 authorization owner: Sales IT
          event for order <n> rejected by CRM: <n> unauthorized
          next: check the technical user's credentials and roles
      1x I06 master data   owner: Transport planning
          <doc> carrier <n> rejected tender: unknown location code AU01
          next: fix the master data or cross-reference, then reprocess
      1x I10 mapping       owner: Integration team
          <doc> unit of measure BX not in code list
          next: extend the mapping or code list, then reprocess
      1x I10 duplicate     owner: Customer service
          <doc> customer reference already used by sold-to <n>
          next: confirm with the sender; cancel the duplicate
      1x I08 master data   owner: Treasury
          statement for account ending <n> not assigned to a house bank
          next: fix the master data or cross-reference, then reprocess
    
    Wrote unit5b/triage_prompt.txt: numbers masked. Paste it only into a model your company approved.
  2. Notice the routing: business categories (master data, duplicate) go to the error owner, technical ones (connectivity, authorization, mapping) to the technical owner. Both come from your inventory; the I06 line shows Transport planning because you fixed that row in Step 4.

  3. Open unit5b/triage_prompt.txt. Customer, order and document numbers are replaced by <NUM> and <DOC>; timestamps and interface IDs stay. This file is what you would give an approved model to suggest causes. The script never calls a model and never reprocesses anything.

Step 8: Save your work in Git

  1. Run:

    git add unit5b/integration_toolkit.py unit5b/calder_interfaces.csv unit5b/spec_I10.md
    git commit -m "Unit 5B: interface inventory, checker and I10 spec"

Leave triage_prompt.txt out of Git: logs, even masked ones, don't belong in a repository.

What the code does

Part What it does
COLUMNS, PATTERNS, SPEC_STATUS, TEST_STATUS, CUTOVER The inventory's columns and the fixed values allowed in its status columns
TEMPLATE Calder's 12 interfaces in the target design, with four deliberate gaps
check_row() Rules per row: required fields, known values, peak versus daily volume, monitoring present, a business error owner for B2B and asynchronous messages, approved spec before "passed", rollback for every cutover change
I10_DETAIL, spec_text() The I10 purpose, mapping, errors, security and tests; the spec layout that fills the rest from the inventory row
MILESTONES, STEPS, cmd_cutover() The data migration topic's cutover milestones, the steps each action implies, and a time-ordered runbook that blocks when a rollback is missing
SAMPLE_LOG, CATEGORIES, signature() A made-up log, keyword rules for error categories, and grouping that ignores document numbers
TECHNICAL Which categories go to the technical owner instead of the error owner
mask() Replaces document and long numbers before any text could leave the company

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 on your path, or the environment is off Turn on .venv (Step 1); on macOS/Linux try python3; see the setup topic
can't open file ... integration_toolkit.py The terminal isn't in the course folder, or the file has another name cd to orchestrate-course; check the file is unit5b/integration_toolkit.py
ModuleNotFoundError A file of yours shadows a standard module, or an old Python Use Python 3.10 or later; don't name your own files csv.py or re.py
calder_interfaces.csv not found Step 3 wasn't run in this folder Run template first
is missing columns or every row shows FAIL A comma was deleted or added while editing Run template --force and redo Step 4, editing one cell at a time
calder_interfaces.csv exists. Add --force You ran template twice Add --force only if you want to lose your edits
Network or proxy errors None expected: the script uses no network If you see one, you ran a different script

The SAP way

What SAP offers for each part of the toolkit, as of October 2026, from the pages opened for this topic. Check your contract: several of these are separate subscriptions or editions.

Toolkit part SAP offering What it holds or does Notes
Inventory and requirements SAP Cloud ALM, Requirements app Integration requirements recorded after fit-to-standard (SAP Learning) Your inventory columns become requirement fields or tags; agree them early
Strategy and interface requests Integration Suite, Integration Assessment ISA-M based recommendations, documentation generation and a business request feature (SAP) Good for "which pattern and technology"; still needs the owner columns
B2B message and mapping specs Integration Suite, Integration Advisor MIGs and MAGs, AI-powered mapping proposals, generated runtime artifacts and test examples, PDF export (SAP, SAP Technology Blog) The mapping document and the running mapping share one source
Partner onboarding Integration Suite, Trading Partner Management Partner profiles, agreement templates, agreements linked to MIGs and MAGs, one generic flow reading the Partner Directory, B2B monitoring, functional acknowledgments (SAP Technology Blog) The 2021 announcement describes the concepts; check today's feature set on your tenant
Monitoring and alerting SAP Cloud ALM, Integration & Exception Monitoring Correlated messages, search by business context, alerts with an Alert Inbox (SAP Support) Coverage per product in the content list (SAP Support)
Business error handling SAP Application Interface Framework Business-user monitoring, correction and mass error handling, role-based field hiding (SAP) Priced per blocks of one million transactions a year on that page; ask SAP which edition applies to your landscape
Method SAP Activate Test levels, quality checkpoints before Deploy, hypercare (SAP Learning) Integration sits in the application workstreams, not apart

Build vs. SAP

Situation Your own files (this topic) SAP tools
Program under 20 interfaces, one partner team Enough: a CSV in Git with a checker, specs generated from it Cloud ALM for requirements if the program already uses it
Many B2B partners with their own message versions Spec skeleton and checks only Integration Advisor and Trading Partner Management hold the message and mapping specs
Several system integrators and a steering committee Checker still useful as a gate Cloud ALM as the shared record, with the inventory columns as fields
Operations after go-live Not a monitor Cloud ALM monitoring and alerts; AIF where business users fix errors
AI help Deterministic grouping and masking first Integration Advisor mapping proposals, Cloud ALM document summary; check what your edition includes

The file-based toolkit doesn't compete with SAP's tools. It makes the decisions explicit (owners, rollback, status values) and gives you checks you can run in a pipeline, whatever tool holds the record.

Architecture view

Where it sits. The toolkit is a design-time and operations layer over the target architecture from the integration platform and clean core:

flowchart LR
  INV[(Inventory:<br/>Cloud ALM or file)] --> SPEC[Specs, MIG/MAG]
  SPEC --> IS[Integration Suite flows]
  IS <--> S4[S/4HANA CP10]
  IS <--> P[Partners, MES, CRM]
  IS --> MON[Cloud ALM monitoring]
  S4 --> MON
  MON --> OWN[Error owners]
  OWN -->|fix and reprocess| IS

Non-functional requirements that decide the design.

Requirement Typical target (course value) What breaks if it's missed
Error ownership 100% of interfaces with a named error owner Failed orders wait until a customer calls
Detection time Alert within 15 minutes of an error on an order interface Hours of orders stuck before anyone looks
Reprocessing Business errors fixed and rerun the same day Late deliveries, duplicate manual entry
Volume Twice the peak hour within the maximum delay Month-end backlog; MRP and billing run on stale data
Data protection Payload retention set per interface; masked logs for AI use Customer data kept or shared longer than allowed
Cutover Rollback ready for every changed interface until the fallback deadline A no-go that can't go back

Integration and identity. Messages carry business identity, not user identity: the partner is identified by its trading partner profile, and a technical user posts into S/4HANA with a role limited to the interface's documents. Monitoring users need roles too: the error owner sees and reprocesses its interfaces' messages, and nobody edits a payload without a log. Business context (order number, customer PO) must travel with the message so Cloud ALM can find it by that value.

The decision to record (short ADR form, see writing a solution design doc):

  • Context. Calder has 12 interfaces, three changing at the July 2027 rollout, and several teams that will own errors. Specs live in documents today and drift from what is built.
  • Options. A: a spreadsheet maintained by hand. B: the inventory as a file in Git, checked automatically, with specs generated from it, and Cloud ALM as the requirement record. C: Cloud ALM only.
  • Decision. B for the program; requirement IDs in Cloud ALM point to inventory IDs.
  • Consequences. One more file to keep in step with Cloud ALM; in exchange, a gate that fails a build when an owner or rollback is missing.

What a design review will ask.

  1. Which interfaces have no approved spec, and which tests ran against drafts?
  2. For each business error, who fixes it, in which tool, with which role?
  3. What is the evidence that each interface holds twice the peak hour?
  4. Which interfaces change at cutover, and what exactly is the rollback for each?
  5. What leaves the company when AI helps with triage, and who approved that model?

Production concerns

  • Security and SAP authorizations. Technical users per interface, never shared, with roles limited to the documents they create. Monitoring roles split by interface and by who may edit payloads. Secrets for partners and APIs in the platform's keystore, never in specs or the inventory. Treat the roles and authorization design topic's rules as applying to technical users too.
  • Evaluation. Measure the workstream with numbers from the inventory: share of interfaces with approved specs, by test level reached, and in hypercare the errors per interface per day and the time to reprocess. Measure AI help the same way: how many drafted test cases a tester kept, how many triage groups matched the real cause.
  • Cost. Separate subscriptions (AIF, Integration Suite editions) and people: every error owner needs time in hypercare. Budget the volume test early; it needs production-like data and a production-sized system.
  • Operations. Handover from project to operations is an inventory review: every owner confirms their rows. Alerts go to teams, not to individuals who leave.
  • Clean core. Specs should name released APIs, events or documented IDocs. A spec that needs custom code inside S/4HANA to read data triggers an architecture decision, as in the integration platform and clean core.
  • Data protection. Payloads with names and addresses stay in monitoring only as long as the retention rule says. Mask before any text leaves for an AI model, and only to a model your company approved.

Pitfalls

  • The inventory as a one-time deliverable. It goes stale the week after the workshop. Make it the input to specs, tests and cutover, so it has to stay current.
  • "Integration team" as the error owner for everything. Business errors then wait in a queue owned by people who can't fix them.
  • No negative tests. Every string test needs the receiver-down and same-message-twice cases.
  • Volume tested with ten messages. Use the peak hour, doubled, with realistic data.
  • Switching off the old interface early. Retire after the fallback deadline, not at the start of cutover.
  • Undrained queues before extraction. Messages still in flight at the load become missing or double open items.
  • Specs that say "map" without a rule. Every field needs the rule and the behavior when the source is wrong.
  • AI output without an owner. A drafted spec or test list that nobody signed is not a spec or a test plan.
  • Unmasked logs in a chat window. Error logs contain customer and document numbers; mask first, approved model only.

Exercise: add I13 to the inventory and write its spec

In the exercise of the integration platform and clean core you added I13, cost centers from CP10 to SAP SuccessFactors Employee Central (the hire-to-retire topic shows what happens when HR still offers a cost center that controlling has closed: S/4HANA rejects the new hire). Now bring it into the integration lead's toolkit.

  1. Open unit5b/calder_interfaces.csv and add a line at the end for I13. Use: name Cost centers to HR, sender S/4HANA CP10, receiver SuccessFactors EC, the pattern and technology you chose in the last exercise, volume 20, peak 20, max delay 1d, business owner HR operations, technical owner HR IT, error owner Controlling, cutover new. Fill every other column, including a rollback, without commas.
  2. Run python unit5b/integration_toolkit.py check and fix every FAIL and WARN on your new row.
  3. Run python unit5b/integration_toolkit.py spec I13 and open unit5b/spec_I13.md.
  4. Replace each _To be written._ with real content: the purpose (which problem from the hire-to-retire topic it prevents), trigger and scope (what about a closed cost center?), a mapping table with at least four rows, an error table with at least three errors and their owners, security notes, and test cases at the unit, string, end-to-end and volume levels.
  5. Run python unit5b/integration_toolkit.py cutover and check that I13 now appears after the go/no-go and at go-live.
  6. Commit the CSV and both specs.

Done when: check prints 13 interfaces: 13 clean, 0 FAIL, 0 WARN, cutover lists I13 steps and ends with "each has a rollback step", and spec_I13.md contains no _To be written._ and has at least three error cases and one test case per level. Keep the inventory and both specs in your portfolio: they are the evidence an integration lead is asked for.

Check yourself

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

1Why does the checker fail a row whose cutover action is "retire" but has no rollback?

Answer: C. Until the fallback deadline passes, the old system may reopen. Without a rollback for each changed interface, a "no-go" restores the system but not its connections.

2In the triage output, the I02 timeouts go to Manufacturing IT, not to Production planning. Why?

Answer: D. The script routes connectivity, authorization and mapping errors to the technical owner and business categories to the error owner. Production planning is I02's error owner, but it can't fix a connection.

3Which pair of cases should every string test of an asynchronous interface include?

Answer: A. Retries and duplicates are where asynchronous interfaces fail in production. The receiver-down case proves retries deliver once; the duplicate case proves no second order is created.

4Why does the I10 spec mark a credit block (E06) as "not an interface error"?

Answer: A. The interface's job was to create the order, and it did. A credit hold is ordinary order-to-cash work for credit management, and routing it to the integration team wastes everyone's night.

5A teammate pastes a raw hypercare error log into a public chatbot to find causes. What is the main problem?

Answer: A. Error logs carry customer and document numbers. The control is to group deterministically first, mask numbers, and use only a model the company approved.

6Why does the toolkit generate the spec's header, volumes and cutover sections from the inventory?

Answer: B. Facts typed twice drift apart. Generating them from the single source of truth keeps owners, volumes and cutover actions consistent; the business sections still need people.

7Calder's I07 extraction is "extend" at the rollout. When is it switched on for CP20?

Answer: B. Extended interfaces are transported switched off and turned on only after go/no-go. Switching on earlier would mix migrated and live data before the decision, and make the way back harder.

8What does SAP Activate require before the Deploy phase, according to SAP's training?

Answer: D. Unit and sometimes string tests run in the Realize sprints, but a full integration test and user acceptance are required before Deploy. For interfaces, that is the end-to-end level.

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