An ITSM is not one thing — it's a system of record plus half a dozen workflows that all read the same CIs, incidents, and changes. The usual result is four engines (a ticket DB, a Neo4j CMDB, a Pinecone similar-incident index, a reporting warehouse) with sync jobs between them, and a "single source of truth" that is really four copies drifting apart. This suite is the opposite: the whole ITSM on one DataK3 bucket. CIs, incidents, problems, changes, services, users and groups are the shared spine; the CMDB blast-radius graph, the SLA clock, auto-triage, problem clustering, the major-incident bridge and CAB change control are workflows that JOIN that spine directly — one copy of the rows, queried by content (SQL), by relationship (graph), and by meaning (vector).

The problem — and the money

An ITSM suite is priced per agent seat — 300–2,000 fulfillers at ~$100–$150/month lands a seven-figure bill before a single custom workflow, and that price buys four systems stitched together. Each workflow here is worth a tutorial on its own, and has one. The point of the suite is that they compose with zero glue: the triage engine reads the same incidents core owns; the SLA clock ticks over those same incidents; the CAB gate scores a change's risk off the same CMDB graph the blast-radius skill assembled. No connector, no nightly export, no reconciliation.

The payoff is the question a stitched stack cannot answer in one query — "if pg-orders-primary fails, what breaks, what has already gone wrong on it, and is the fix-it change safe to ship?" On DataK3 that is three JOINs over one copy of the rows: the database's blast radius of 10 CIs across all 4 business services (max hop 3), the four incidents on that CI that clustered into a single known error (PRB2001), and the CAB verdict that the same blast radius makes the permanent fix a cab_review, not an auto-approve. One bucket, three pillars.

Seven base skills, each independently live-validated, compose into the suite:

#SkillWhat it ownsReads (shared master)
1itsm/corecis, incidents, problems, changes, services, users, groups, ci_edges, incidents.embedding— (the root)
2itsm/cmdb-blast-radiusimpacts, service_map, impact_analysis, the graphs cmdb/cmdb_impactcis, ci_edges, services
3itsm/sla-managementsla_definitions, incident_sla, sla_breachesincidents, services, groups
4itsm/incident-managementincident_triage, incident_work_notesincidents, cis, services, groups, incidents.embedding, cmdb_impact
5itsm/problem-managementproblem_incidents, known_errorsincidents, problems, changes, incidents.embedding
6itsm/change-managementchange_approvals, change_calendar, change_policychanges, cis, services, cmdb_impact
7itsm/major-incidentbridges, bridge_eventsincidents, cis, services, cmdb_impact

This page does not re-teach them — follow each link for the mechanics. Here we show how they assemble into one bucket. The machine-readable manifest is the suites/itsm contract.

The components teach; the suite ships

Each of the seven posts above is a standalone package you can read and run — that is what they are for, and each component's own download is still exactly that. But the deployable artifact is the suite. code/itsm-suite-app is the whole ITSM as one app: a single FastAPI with a router per component (mounted under /core, /incident-management, /problem-management, /change-management, /major-incident, /sla-management, /cmdb-blast-radius), one canonical models.py covering all 23 tables, and plain imports — no importlib loader — over one bucket and one dodil-appid pool, so seven components mean one sign-in and one bill. That is what you fetch to stamp a customer system; the component posts are what you read to know why each part is shaped the way it is, and how to derive a different customer's version. It ships by the ordinary git cycle (repo → CI → registry → CD): Ship a DODIL app. The tar also carries PLATFORM.md — the platform invariants, so whoever downloads it gets the rules along with the code.

How it composes

Three rules make seven skills one ITSM.

1. One bucket, 23 disjoint tables. Every skill's tables are globally unique names, so they coexist in a single bucket with no collision — 8 core + 3 cmdb-blast-radius + 3 sla + 2 incident + 2 problem + 3 change + 2 major-incident. incidents.embedding is a column, not a table; cmdb/cmdb_impact are graphs. Installing the suite is installing the seven skills, in order, into the same --bucket. The bucket here is literally named itsm — four characters, so unlike the GL suite's two-character gl (which had to become gl-suite) it needs no suffix; DataK3 requires a bucket name of at least three.

2. Masters are owned once, joined everywhere. itsm/core owns the seven masters. No other skill re-declares them — they JOIN them. The triage engine writes incident_triage keyed to the same incident_id core owns; the SLA clock keys to that same incident; the CAB verdict keys to the same change_id; the bridge resolves that same incident. One row, many readers — and one live-found reconciliation: service_id is a string key on cis, incidents and services, so the impacted-service rollup JOINs cleanly across all three:

You

In the itsm bucket, show the cross-skill joins that prove shared masters: incident-management's incident_triage joined to core's incidents, and change-management's change_approvals joined to core's changes with the blast radius the cmdb graph produced.

DODIL MCP tools called
data_sql
Agent

incident_triage ⋈ incidents: INC1009 = triaged / database / P1 / g-dba, classified by kimi-k2.6 at confidence 0.90 — the triage engine wrote against the same incident_id core owns, and advanced its state. change_approvals ⋈ changes: CHG5001 = cab_review / high, 4 impacted business services, blast_radius_json spanning 9 CIs — the CAB verdict keyed straight to the change core owns, its risk derived from the cmdb_impact graph. No copy, no join table between products.

3. One graph, assembled once. This is the subtle one. cmdb and its reverse cmdb_impact are created from a node table (cis) and an edge table by a CREATE GRAPH that snapshots its edges at creation — edges added afterwards are invisible. In v1 the only contributor of CI edges is itsm/core, so the single CREATE GRAPH is deferred to itsm/cmdb-blast-radius at position 2 (right after core loads every edge) — core inserts nodes+edges but runs no CREATE GRAPH. Then the blast radius traverses it — and it must be typed, or part_of composition leaks in and over-counts:

You

In itsm, run the single deferred graph assembly (cmdb + the reverse cmdb_impact) after core has loaded all ci_edges, then show the blast radius of pg-orders-primary — and prove the traversal is typed by narrowing impact_rels to exclude part_of.

DODIL MCP tools called
data_pg
Agent

cmdb_impact assembled once over core's fully-loaded 14 ci_edges. Blast radius of pg-orders-primary (CI 1) = 10 CIs across all 4 business services, max hop 3: hop 1 checkout-api / orders-api / accounts-api / reporting-etl / pg-orders-replica, hop 2 payments-gateway / web-storefront / search-index, hop 3 mobile-app-bff / cdn-edge. The typed subtlety, found live: rebuild impacts with impact_rels excluding part_of and the same blast is 9 CIs — Online Checkout falls from 5 to 4, because mobile-app-bff hangs off web-storefront by a single part_of edge and is reachable no other way. Type your traversal.

IMPORTANT

The composition subtlety, found live. A traversal that is correct for one skill can be wrong once the suite shares its graph. impacts is built only from rels in impact_rels (default: all three). A blanket reverse over every ci_edges row over-counts when part_of (composition) shouldn't propagate failure — the ITSM analog of the CRM family-rollup bug where an untyped hop leaked partner_of edges ($66k, not $36k). Live-proven on this estate: pg-orders-primary's blast is 10 CIs with all three rels but 9 once part_of is dropped, and Online Checkout falls 5 → 4mobile-app-bff reaches the graph by exactly one part_of edge to web-storefront and by nothing else. One CI and one business service is the difference between paging the right team and paging everyone. Assembling into one graph is the whole point — but every graph read must say which edges it means.

What composing actually caught

The argument for a suite is usually stated as convenience — one bucket, one deploy. That undersells it. When the seven components were finally stood up together on one bucket (2026-09-08), six integration bugs surfaced that every component had hidden while it ran alone, because alone each one was internally consistent and passed its own ## Test:

  • pgvector.sqlalchemy.Vector cannot be read back through the ORM on DataK3. Over a binary-format result — which is what the ORM uses — DataK3 returns a VECTOR column as a native float array, so psycopg hands SQLAlchemy a list while upstream's result processor expects the text form and calls .split() on it. Every session.get(Incident, …) in the suite died with AttributeError: 'list' object has no attribute 'split'. Writes are fine; a raw text("SELECT embedding") is fine; a package whose incidents stub omits the column never selects it at all — which is exactly why nobody saw it until the models were unified. Fixed with a tolerant Vector subclass, byte-identical in all four models.py that declare a vector column. This one is platform-wide, not ITSM-specific — it hits any DODIL build whose ORM selects a vector column — and the subclass is a workaround for a filed platform issue, not the end state. → itsm/core
  • Two schema drifts. incidents.service_id was BigInteger in one package and String in every other (the masters are strings — svc-orders); and changes.type was mapped as the attribute change_type, which breaks db.upsert with an "unconsumed column names" error and breaks the read-merge-upsert idiom {c.name: getattr(row, c.name) …} with an AttributeError. Canonical rule now stated in models.py: the ORM attribute name IS the column name. One canonical models.py in the suite app is what makes the drift impossible in the first place. → itsm/incident-management, itsm/problem-management
  • A read-your-writes assumption. cluster's link_change path read a row it had written in the same open transaction — DataK3 stages the write log until commit, so s.get(Problem, pid) returned None and it died on 'NoneType' object has no attribute 'id'. Fixed with a commit between the write phase and the back-link phase: two transactions, not one. → itsm/problem-management
  • A state machine with no reachable entry. The problem engine spawned its permanent-fix change in state='new', and change management only assesses from {'assess', None} — so the change a problem exists to deliver could never clear the CAB: 409 change is frozen in state 'new', forever, silently, on every retry. Both components were correct alone; together they were a deadlock, and the failure was a stable 409 rather than a crash, so nothing alerted. → itsm/change-management
  • And the one worth remembering: one ticket took the SLA engine down for the whole estate. Core's create route did not stamp opened_at, so a ticket landed with a NULL open time. The clock computes both due dates as opened_at + minutes across every open incident, so that single row raised a TypeError and every subsequent tick 500'd — freezing everyone else's breach flags. The dashboard did not go red. It went stale, and stale looks exactly like "nothing is breaching". Fixed on both sides: the producer stamps t0, and the clock now skips and counts uncomputable rows (skipped_no_clock), because a per-tick engine must never be one bad row away from stopping. → itsm/sla-management

Two further findings came from the bridge and the clock finally sharing a bucket. The clock never retired a ticket somebody else resolved — INC1006 still read in_progress, resolve_breached, escalation_level 2 long after the major-incident bridge had closed it, because the evaluation loop only walks open incidents; tick now has a retire pass. And a resolved breach paged a manager forever: the on-call rollup grouped unnotified breaches with no reference to whether the ticket was still open, so INC1006's level-2 breach sat on [email protected]'s list after the bridge stood down. /sla/oncall now joins incidents — while the sla_breaches log itself is deliberately untouched, because it is an audit record: "what happened" and "what do I owe right now" are two different questions over the same table.

None of these are exotic. They are the ordinary consequence of components that share tables being verified in isolation, and the lesson is worth more than any individual fix: components validated separately are internally consistent and still wrong together. That is the reason this suite is presented as one system rather than seven tutorials that happen to sit in the same folder.

Auth — config at the edge, a role gate in the app

On Ignite, end-user login is configuration, not code. The suite deploys with the itsm-suite dodil-appid pool attached (user_pool: itsm-suite in .dodil/deploy.yaml) and the per-cluster Ignite gateway runs the entire browser login at the edge — PKCE S256 against the pool issuer, an AEAD-sealed host-only session cookie, single-flight refresh, EdDSA verification against trust anchors this app does not hold — then injects the verified identity into every request it forwards: X-Dodil-User (sub, email, connection, app_roles), X-Dodil-User-Jwt (the raw verified token, carrying the catalog-expanded permissions claim) and X-Dodil-Auth-Source (pool for an app end-user, platform for an operator or service-account invoke). Any inbound copy of those headers is stripped first, on every mode and every principal, so a caller can never forge them.

What survives in the package is a small auth.py that ships no verifier — no JWKS client, no issuer/audience env, no crypto dependency, and no pyjwt in requirements.txt. It reads the injected header and keeps the one job the app still owns: role-based gating.

Permissions are namespaced <module>:<object>:<verb>, so one customer pool carries every ERP module's roles without collision (itsm:change:approve is not crm:change:approve). Across all seven components the audit left exactly four gates standing:

PermissionGatesWhy this one and not the rest
itsm:change:approvechange-management: assess, transition, and the change-policy upsertaccepting the risk of a production change. change_approvals.decided_by records the gateway-vouched user — and editing the policy is editing the gate, so it needs the same permission as passing it
itsm:major:declaremajor-incident: declare a bridgedeclaring a major incident pages the org. bridges.declared_by records who
itsm:incident:resolvemajor-incident: the bridge-state transitionthe only route in the module that writes incidents.state='resolved' — which is what stops the SLA clock. Deliberately separate from itsm:major:declare: whoever declares an incident is not automatically whoever may call it over
itsm:cmdb:rebuildcmdb-blast-radius: the graph assemblyit TRUNCATEs impacts + service_map and DROP+CREATEs both graphs. Every other component's blast radius is whatever this route leaves behind, and a half-assembled graph fails quietly — a smaller blast radius reads as good news

Four of the seven components ended with zero gates, on purpose: itsm-core, itsm-incident-management, itsm-problem-management and itsm-sla-management. CMDB CRUD, triage and clustering are the service desk's ordinary work — a permission that every agent on the desk must hold is ceremony, and ceremony is exactly what an auditor discounts. Three gates the pre-conversion design had predicted were deleted in the audit: incidents:triage, problems:write, and cmdb:write (that last one guarded an idempotent per-CI precompute, which re-running changes nothing). One gate the roadmap had not predicted was added: itsm:cmdb:rebuild. The audit's question was never "is this a write?" but "does this accept risk, page people, stop a clock, or destroy something?"

The pool is created once for the whole suite, with the role catalog those gates check:

You

Create a dodil-appid pool itsm-suite with email+password, and set its role catalog: a service-desk agent needs no special permission; a change manager may approve changes; an incident commander may declare a major incident and resolve it; a CMDB admin may rebuild the graph.

Agent

Pool itsm-suite created — issuer https://appid.dodil.io/ihdiash/itsm-suite, audience pool:itsm-suite, email+password (local) enabled. Catalog set: agent = (no permissions); change-manager = itsm:change:approve; incident-commander = itsm:major:declare, itsm:incident:resolve; cmdb-admin = itsm:cmdb:rebuild. A user's next token carries app_roles plus the catalog-expanded permissions claim the gates read.

Note that agent — the service desk, the largest role by headcount — holds no permissions at all. It does the ungated work, which is most of the module.

The two-plane rule is unchanged: the pool identifies the user; the app still reaches DataK3 through its own service account (sa_token.py mints and refreshes a client_credentials token for the pg-wire password) — an app-user is never a bucket principal. Locally, with no gateway in front of uvicorn, opt in to a stub identity with DEV_ALLOW_ANON=1; the stub carries no permissions unless you grant them (DEV_USER_PERMISSIONS=itsm:change:approve,…), so the gated routes stay gated on a laptop too. Today the pool is email+password (local); oauth/oidc/saml corporate SSO switch on per pool later, with no app change. Full flows: App authentication and App roles.

The route with no identity at all

POST /sla/tick carries no identity dependency whatsoever — not a permission, not even Depends(current_user). It is a machine heartbeat, called by a service account over a platform invoke, which carries no X-Dodil-User at all. A current_user dependency there would 401 the clock: the engine would stop and every incident's breach flags would silently go stale — the failure mode of an SLA system that reports green. Exposure is the ingress's job (public_invoke=false), not a user permission. This applies to every recompute route in every module — a rollup, a re-materialization, an embedding backfill: if a machine calls it, gate the door, not the caller.

The scheduler — something has to drive the clock

Ignite is request-invoked, and there is no server-side scheduler. That is fine for every other workflow here, which is triggered by a person or an event. It is not fine for the SLA clock: an SLA clock that only advances when somebody loads a page is not an SLA clock. A deployment must pick one of two patterns, and say which:

  • (a) an always-on pinned app — deploy with --reserved 1 --max-replicas 1 and run a loop inside the pod that calls tick() on an interval. Self-contained, and the clock never depends on anything outside DODIL; in exchange the app never scales to zero, so it costs a warm replica.
  • (b) an external scheduler — cron, a CI timer, or any orchestrator POSTing /sla/tick. The app stays scale-to-zero, but the clock now depends on infrastructure outside DODIL.

Either way the caller is a service account over a platform invoke, not a browser user — which is precisely why /sla/tick must not sit behind current_user. This is a genuine platform gap rather than a design preference, and ITSM is where it is most visible to a buyer: everything else on the platform can be button-triggered; a contractual SLA clock cannot. Full treatment in itsm/sla-management.

Scaffold it — the one-shot

With the DODIL MCP connected, one prompt scaffolds the full suite in order into one bucket:

Scaffold the full ITSM suite in one bucket (suites/itsm).
 
Bucket: itsm. Seed the shared storefront demo. Install the seven base skills IN ORDER into that one bucket:
  1. itsm/core             — 8 masters + ci_edges + incidents.embedding VECTOR(2048). service_id is a STRING
                             key on cis/incidents/services. Stamp opened_at on every incident created.
                             Populate cis+ci_edges but DEFER CREATE GRAPH.
  2. itsm/cmdb-blast-radius — build service_map + the reverse impacts (typed to impact_rels), then run the
                             SINGLE CREATE GRAPH cmdb + cmdb_impact over core's fully-loaded edges.
  3. itsm/sla-management   — SLA policy + the clock over core.incidents (pure SQL, no Models). /sla/tick is
                             ungated: it is a machine heartbeat, not a user action.
  4. itsm/incident-management — dedup + triage over incidents.embedding + cmdb_impact; start the SLA clock.
  5. itsm/problem-management — cluster incidents by embedding into problems + known-errors; spawn the
                             permanent-fix change in the state change-management assesses from.
  6. itsm/change-management — CAB gate; risk = the cmdb_impact blast radius; deterministic freeze floor.
  7. itsm/major-incident   — declare a bridge off an incident, scope it from cmdb_impact, run
                             open -> mitigating -> resolved, and resolve the underlying incident.
 
Then prove the assembly: 23 tables coexist; incident_triage⋈incidents and change_approvals⋈changes join;
the blast radius of pg-orders-primary is 10 CIs across 4 business services; a P1 SLA breach exists and is
retired once the bridge resolves it. Ask me the union questions once (bucket, seed, overlay, impact_rels,
categories) — don't re-ask what composition already answers.

Want an industry cut? Add overlay: finserv | manufacturing — an additive diff on top of the same 23 tables (a compliance hard gate + change_audit, or OT CIs + maintenance_windows), never a fork.

Building just part of it

You don't have to install all seven. To build a subset, install itsm/core first — it owns the seven masters + ci_edges + incidents.embedding every workflow reads — then add only the workflow skills you want. Each workflow tutorial names its master dependencies (its skill contract's consumes block is the machine-readable list): incident-management reads incidents/cis/services/groups; change-management reads changes/cis/services; major-incident reads incidents/cis/services plus the blast graph. Each workflow can also run fully standalone on an empty bucket — it ships a stub-masters step that creates just the masters it reads — but once two workflows share the same masters, install itsm/core once instead of letting each stub its own. (Stubbed masters are precisely how two of the six integration bugs above stayed hidden.) And skip the graph (no CREATE GRAPH) unless you install itsm/cmdb-blast-radius, which owns the single deferred assembly; incident-, change- and major-incident management degrade gracefully without it (no-blast triage; the non-graph risk floor).

The questions, asked once

Composition removes the redundant asks. The suite interview is short:

  • Asked once (shared): the bucket, seed_data, the industry overlay, impact_rels (shared by cmdb-blast-radius, change-management's risk and major-incident's scope), and categories (shared by the incident triage gate and the problem RCA gate — one answer, both gates).
  • Removed by composition: incident/problem/change/major never ask "hand-feed incidents or seed?" — their incidents and changes come from core; change-management never asks "build a CMDB?" — the graph comes from cmdb-blast-radius; incident-management never re-declares the SLA clock table — it's owned once by sla-management.
  • Still per-workflow (genuine knobs): the priority_scheme, the change auto_approve_risk, the sla_targets, the freeze_windows, the cluster_min, the bridge severity_scheme. Real best-practice decisions — the overlay defaults them, you tune them.

Verify

The full single-bucket assembly was live-validated on 2026-09-08 (org IHDIASH, bucket itsm, left up) — the composition itself, not a re-test of each skill. The estate is 13 CIs / 14 typed edges / 4 business services / 5 groups / 13 incidents / 5 changes:

# 1) 23 tables from all seven skills coexist in ONE bucket, NO name collision
dodil data table list -b itsm    # 23 (8 core + 3 cmdb + 3 sla + 2 incident + 2 problem + 3 change + 2 major)
 
# 2) shared master, two skills: incident-management's incident_triage ⋈ core's incidents
dodil data sql -b itsm "SELECT i.state, t.category, t.priority, t.assignment_group, t.model_id
  FROM incident_triage t JOIN incidents i ON i.id = t.incident_id WHERE t.incident_id = 1009"
#   triaged | database | P1 | g-dba | kimi-k2.6      (precedent: INC1003 / INC1005 / INC1004, all g-dba)
 
# 3) the vector pillar answered before the model was called: INC1011 deduped against INC1010
dodil data sql -b itsm "SELECT is_duplicate, master_incident_id, rationale
  FROM incident_triage WHERE incident_id = 1011"
#   true | 1010 | "Near-duplicate of open incident 1010 (cosine 0.079 < dedup_threshold); linked, not
#                  triaged fresh."   -> no classification call, no token spend
 
# 4) ONE graph, assembled once by cmdb-blast-radius; the typed blast radius of pg-orders-primary
dodil data sql -b itsm "SELECT impacted_ci_count, max_hop, impacted_services_json
  FROM impact_analysis WHERE ci_id = 1"
#   10 | 3 | ["Customer Accounts","Internal Reporting","Online Checkout","Order Management"]
#   (9 CIs once part_of is dropped — Online Checkout 5 -> 4, mobile-app-bff falls out)
 
# 5) problem clustering over incidents.embedding: 4 incidents under one problem, one of them
#    triaged 20 seconds earlier by a DIFFERENT component
dodil data sql -b itsm "SELECT incident_id, ROUND(similarity,4) FROM problem_incidents
  WHERE problem_id = 2001 ORDER BY incident_id"
#   1003 0.0000 · 1004 0.0813 · 1005 0.1416 · 1009 0.1094      (all inside the 0.25 radius)
dodil data sql -b itsm "SELECT number, state, known_error, related_change_id FROM problems WHERE id = 2001"
#   PRB2001 | known_error | true | 3001    (KE2001 published, CHG3001 spawned as the permanent fix)
 
# 6) the CAB scored those changes off the SAME graph — and the freeze floor overrode the model
dodil data sql -b itsm "SELECT change_id, decision, risk, impacted_service_count FROM change_approvals
  ORDER BY change_id"
#   3001 cab_review medium 4 · 5001 cab_review high 4 · 5002 auto_approve low 0
#   5003 cab_review high 1   · 5004 cab_review high 2   <- freeze-window conflict, deterministic floor
 
# 7) the SLA clock over shared incidents — three consecutive ticks, byte-identical
#   {"evaluated":6,"breaches":2,"escalations":1}   incident_sla 6 rows, sla_breaches 2 rows
#   INC1006 (P1, 240-min resolve target) resolve_breached, escalation_level 2 -> [email protected]
#   INC1007 (P3, new for 3h against a 60-min response target) response_breached
 
# 8) and the two components moving together: the bridge resolves the incident, the clock retires it
dodil data sql -b itsm "SELECT bridge_id, severity, state, impacted_ci_count, impacted_service_count
  FROM bridges WHERE bridge_id = 'mi-1006'"
#   mi-1006 | SEV1 | resolved | 5 | 1        (Online Checkout — scoped from cmdb_impact, not guessed)
#   the next tick then read evaluated 5, escalations 0, retired 1

The 23-table coexistence, the cross-skill JOINs, the single CREATE GRAPH, the typed blast radius and its part_of contrast, the dedup that never called a model, the four-incident cluster, the CAB verdicts with the freeze floor overriding the model, the SLA breach and its retirement by the bridge are all proven live. The Ignite engines each skill deploys are validated in their own tutorials; the suite test is the data-plane composition — that seven skills share one bucket, one set of masters, and one CMDB graph without collision.

NOTE

The bucket is left up and still being written to, so the counts above are a record of the 2026-09-08 validation run rather than a snapshot you can expect to reproduce byte-for-byte today — later tickets and later ticks have moved the SLA rows on. The structural facts (23 tables, the 10-CI blast radius and its 9-CI typed contrast, the cluster membership) are functions of the seed data and do reproduce.

Connect your tools

Everything the seven skills wrote lives in the one DataK3 bucket, reachable by your own stack — psql and pgvector drivers over the Postgres wire, a Neo4j driver over Bolt against cmdb/cmdb_impact, gRPC for the table engine. data connect itsm prints the endpoints; point BI, a dashboard, or your app straight at the live rows. Full, live-validated walkthrough: Connect your tools.

Composes

This page is a composition, not a fork:

  • Seven base skillsitsm/core, itsm/cmdb-blast-radius, itsm/sla-management, itsm/incident-management, itsm/problem-management, itsm/change-management, itsm/major-incident — each its own tutorial and its own ## Test.
  • The suite manifest (suites/itsm) — the ordered install DAG, the shared-master wiring, the single deferred graph rule, and the union Q&A above.
  • The suite package (code/itsm-suite-app) — the seven components as one deployable FastAPI, one canonical models.py, one bucket, one pool.
  • Overlays (itsm/overlays/*) — additive industry diffs (finserv, manufacturing) that apply on top of the assembled suite.

Read the seven to learn each workflow; read this to assemble them into one ITSM on one bucket.