TeamcenterKnowledge

Skills

TC Decision Management

Skill tc-decision-management. Author decision management (ISO/IEC/IEEE 15288:2023 clause 6.3.3) in Teamcenter - trade studies, measurable selection criteria frozen before the alternatives exist, alternative evaluation, and a decision record carrying resolution, rationale and assumptions. Covers building criteria on Parameter Management's OOTB registry, the difference between a type being absent from a TIER and absent from the RELEASE and which call answers which, authoring a decision on stock types before the typed template is deployed, and the nine silent-200 traps hit while proving it (createRelations reporting no created uid on relations it created, deleteObjects hiding partialErrors from a non-DBA account, setProperties silently writing nothing because it takes info/vecNameVal and not objects/attributes, a parallel-array file-ticket lane, and a workflow template that holds its name while being unlistable). Use for trade studies, decision records, criteria registries, weighted alternative evaluation, rationale and assumption capture, or deploying the Decision Management module.

Clause 6.3.3 wants four things recorded: that a decision needed alternative analysis, that the alternatives were evaluated, that a preferred course of action was selected, and that the resolution, rationale AND assumptions were written down. The third of those is the one usually missing, and an assumption that was never written down cannot be revisited when it turns out to be wrong.

Verified live on cloud2506 (TC 2506.0004) on 2026-08-08. Every uid and item id below was returned by a tool call and re-fetched.

What serves clause 6.3.3 on a stock tier

Decision management is served by the tc-decision-management module: a BMIDE template plus Active Workspace surfaces and a workflow, deployed through Deployment Center. This skill is how to author against it, and how to author a decision on stock types before that template is deployed.

Two things worth knowing before you start.

★★ Criteria have an OOTB home already, under a name you would not search for. Att0ParamDictionary plus Att0AttributeDef is Parameter Management's registry of measurable terms with real units of measure, live and populated on a stock tier (44 definitions and 183 parameter instances on the tier this was built against). Build criteria on it rather than inventing a parallel store: a criterion then carries a genuine unit and participates in Parameter Management like any other measurable characteristic. The typed Decision Support data model binds its own criterion definitions to Att0AttributeDefRevision for the same reason.

★★★ Establish what a BMIDE template IS before you build a capability claim on it. A decisionsupport tree found on a local disk carries DSSDecision, DSSCriterion, DSSAlternative, C++ handlers, an AWC kit and a Deployment Center kit, and reads exactly like a shipped product. It is an in-house prototype, unmatured, written against TC 2512. Finding source code tells you someone modelled the domain, not that it deploys anywhere. Check who ships it, which release it targets, and whether it has ever been installed.

★★★ "Not on this tier" and "not in this release" are different findings

getTypeDescriptions2 returning an empty entry tells you a type is not on THE TIER YOU ASKED. It does not tell you whether it exists in the product.

Question What answers it
Is this type on THIS TIER? getTypeDescriptions2. An empty entry means absent here.
Does it exist in the RELEASE? The per-release data model report, or a BMIDE model export.

Those lead to opposite next steps: install the application, versus build it. The call looks identical either way. Confirmed for the DSS* types: absent from the 2506 and 2606 data model reports, and absent from all 8,571 business objects in a 2506 BMIDE export.

A tier query establishes deployment, a data model catalogue establishes existence, and a documentation survey establishes neither.

The no-BMIDE data model

6.3.3 concept OOTB carrier Notes
Decision Item / ItemRevision, id DM-DEC-nnnn Revision = one decision cycle
Criteria Set Item / ItemRevision, id DM-CRS-nnnn Released = frozen
Criterion Att0AttributeDef in an Att0ParamDictionary Real unit of measure
Alternative Item / ItemRevision, id DM-ALT-nnnn Eliminated ones stay in the register
Strategy / rationale / assumption Item, ids DM-STR- / DM-RAT- / DM-ASM- Full text in an attached Text dataset
Decision Record Dataset (Text) with real file content TC_Attaches to the Decision revision

Relations, since without BMIDE there is no Dm0HasAlternative to create:

Decision rev --FND_TraceLink-->      Alternative rev     (DM-ALT-)
Decision rev --IMAN_specification--> Criteria Set rev    (DM-CRS-)
Decision rev --IMAN_reference-->     notes               (DM-RAT- / DM-ASM- / DM-STR-)
Decision rev --TC_Attaches-->        record datasets
Alternative  --FND_TraceLink-->      the artefacts it is an alternative FOR

ItemRevision -> POM_object is a declared GRM row for IMAN_specification, IMAN_requirement and IMAN_manifestation, which makes those three universal from any item revision to anything. FND_TraceLink, IMAN_reference and TC_Is_Represented_By also work ItemRevision-to-ItemRevision. All six were created and read back live. The id prefix on the target is what tells the roles apart.

Fnd0CustomNote is the semantically right carrier for a rationale and it is unusable here. The type carries naming rule "CSTMNOTE-"nnnnnn with no counter configured, so a supplied id is rejected as not matching the pattern and an omitted id cannot be generated either. That is a site configuration gap, not a property of the type. Worth retrying on a tier where the counter exists.

Freeze is a release status, not a boolean

Clause 6.3.3 b) orders the tasks: criteria are declared at b2, the trade space and alternatives are identified at b3. That ordering is the control against fitting criteria to a preferred answer, so it should be checked mechanically rather than asserted.

A frozen: true flag in a YAML file is a boolean a committer can flip either way. Teamcenter already has something stronger: release the Criteria Set revision. date_released is stamped by the server, the status is attributable, and a later change needs a new revision, which leaves a dated revision history that cannot be rebased.

Then compare that date against the creation_date of every Alternative and report the ordering as a finding. An unfrozen set and a set frozen too late are different failures with different remedies, so do not collapse them into a pass/fail boolean.

Release without a workflow: Workflow-2007-06-Workflow/setReleaseStatus. All three ReleaseStatusOption attributes are required even when empty, and only Append is supported. Status type names verified live on cloud2506: TCM Released, Released, Pending, Prototype, Production, Baseline, Obsolete, and SKW4_ReleasedFML (which displays as "Released FML"; pass the name.db value, not the display string).

Criteria as parameter definitions: the crack

This is what makes "measurable selection criteria" (b2, NOTE 4) real rather than prose. A criterion is an Att0AttributeDef carrying a genuine unit of measure, and the DSS template independently binds DSSCriterionParameterDef to Att0AttributeDefRevision, so this is the same channel Siemens uses.

★★★ att0AttrType on the REVISION is REQUIRED and has no default. Omit it and the create fails with:

The specified name NULL is invalid for a type
Unable to create Business Object.

which names no property and reads like a broken business object rather than a missing field. Valid values are the Rich Client Data Type list: Value, Double, Integer, Boolean, String, Point. It is a type name because it decides which Att0MeasurableAttribute* subclass a value materialises as.

★★ uom_tag goes on the ITEM. The same unit on the revision create-input as att0Uom faults 38015 Unable to find a property with name Att0AttributeDefRevisionCreI/att0Uom. The revision ends up carrying att0Uom anyway, derived from the item. Confirmed both directions on the same tier in the same minute.

The working create:

{"inputs":[{"clientId":"c1",
  "createData":{"boName":"Att0AttributeDef",
    "propertyNameValues":{"item_id":["DM-CRI-0001"],"object_name":["system-mass"],
                          "uom_tag":["<UnitOfMeasure uid>"]},
    "compoundCreateInput":{"revision":[{"boName":"Att0AttributeDefRevision",
      "propertyNameValues":{"item_revision_id":["A"],
                            "att0AttrType":["Double"],
                            "att0Application":["Engineering"]},
      "compoundCreateInput":{}}]}},
  "dataToBeRelated":{},"workflowData":{},"pasteProp":"contents",
  "targetObject":{"uid":"<Att0ParamDictionary uid>"}}]}

Resolving a unit: match on SYMBOL, never on the description. UnitOfMeasure has no object_name, so a type query returns zero for it; use the site's own UOM saved query (ADP UOM Query on this tier, entries Symbol and Description). cloud2506 carries 655 UOM objects and two kilogram rows, kg ("Kilograms", the UMS row) and KG ("KG - Kilogram", a legacy import). Picking the wrong one silently decides which unit population is authoritative. Verified uids on cloud2506: kg Q7nlDS3_Z$cfED, USD g3ppyQr4Z$cfED, min gPnlDS3_Z$cfED, h gPllDS3_Z$cfED, % Q$rlDS3_Z$cfED, Unitless gDllDS3_Z$cfED.

Bind to an engineering characteristic, never to a datatype. A dictionary whose definitions are named Real, Integer and Boolean collapses every criterion onto one unitless term and destroys the measurability the clause is asking for.

Att0AttributeDef uniqueness is on NAME PLUS UNIT, and it says so differently. An item-id clash reports is not unique; a definition clash reports An attribute definition with name "X" and unit "Y" already exists. A get-or-create that only matches the first string treats a normal re-run as a hard failure.

Prose does not fit, and the failure is not truncation

★★ Every string property that could hold a paragraph caps at 240 characters on Item and on Fnd0CustomNote alike, and there is no long-text alternative short of a BMIDE change. Overrun does not truncate: it faults 515035 and loses the whole compound create, halfway through a provisioning run.

So a rationale, an assumption or a decision statement goes into a Text dataset with real file content, and the description carries a summary plus a pointer. That is not a workaround. A decision rationale that exists only as a 240-character summary is not a recorded rationale.

★★★ Blank means two different things, and they need different renderings

The single most reusable thing to come out of building this, and it is not Teamcenter-specific. It showed up three separate times in one day, in three unrelated places, and each one produced a wrong conclusion before it was caught.

What was blank What it was mistaken for What it actually was
A dataset attached to a revision a successful check-in created, attached and empty
Two Active Workspace panel sections defects to fix the sheet correctly reporting absent data
A relation-backed objectSet no linked objects the relation not reaching the XRT layer at all

Absence of data and failure to ask are different facts. Rendering both as nothing makes them indistinguishable, and the reader has no way to tell which one they are looking at, so they guess, and half the time they guess wrong.

Whenever you build something that can return nothing, make "nothing" say which nothing it is. Concretely, in the three places this module touches:

  • A dataset check-in is verified by downloading the bytes back and comparing, never by reading ref_list and never by the create response.
  • An Active Workspace section under commissioning carries a <property name="object_string"/> diagnostic, which separates "wrong registration" from "content not resolving" in one render instead of two and a guess.
  • A panel that traverses a relation renders three distinct states: the rows, an explicit "nothing is linked" sentence, and an explicit error naming the relation and the fault. Never a blank for any of them. See tcdm/awc/HTMLPANEL-SPEC.md.

The same shape recurs across this whole stack under other names: a filtered view reporting like a complete one, a cold cache reporting like a fix, a saved query reporting zero for a type that exists, an empty 200 reporting like a success. They are all one failure: a signal that cannot distinguish two states being read as if it could.

The eight silent-200 traps, all measured here

These cost the most time, and every one of them returns HTTP 200.

  1. ★★★ createRelations reports NO created uid for relations it created. The new relation is in output[0].relation.uid; ServiceData.created comes back empty. Reading created and raising on empty declared five relations failed that had all been created correctly. Verify by reading the reference property back off the primary, not by trusting the response's most convenient field.

  2. ★★★ A non-DBA account's deleteObjects hides its partialErrors. As an ordinary user it returned a bare ServiceData with no deleted list, no partialErrors and no fault, having deleted nothing. The same call on the same objects as the DBA account returned deleted: 19 and, when it failed, the real reason: 515110 "The instance is referenced", 51010 "does not have delete access". A clean delete report from a non-privileged account is worth nothing.

  3. ★★★ setProperties takes info/vecNameVal, NOT objects/attributes.

    CORRECTED 2026-08-09, and the earlier version of this entry was wrong. It said the fix was to read lsd off the object and pass it as a top-level timestamp. That does not write. A second session acted on it, could not make it work on two tiers, and briefly published that its tier was create-only. Both of us had the same real bug: the member names.

    Core-2010-09-DataManagement/setProperties takes info, a list of PropInfo. A body built from objects and attributes contains no info, so the operation parses, finds nothing to update, and returns 200 with no error and no write. Adding a timestamp to that body changes nothing because the body was never the right shape. This is the full-struct rule biting on the OUTERMOST member, where it is hardest to see: everything looks plausible and the call succeeds.

    The body that works, verified on vm2606 by read-back, twice, including a two-property write and an advancing last_mod_date:

    POST Core-2010-09-DataManagement/setProperties
    {"info":[{"object":    {"uid":"<uid>","type":"<Type>"},
              "timestamp": "",
              "vecNameVal":[{"name":"object_desc","values":["new value"]},
                            {"name":"object_name","values":["new name"]}]}]}
    

    Three traps inside that one body:

    • ★★ timestamp goes INSIDE each info entry, not at top level, and an empty string is fine. A top-level timestamp is not a member of SetPropertiesInput and faults the JSON parser. You do not need lsd.
    • ★★ Omit options entirely. "options":[] and "options":[""] both fault the JSON parser. Same shape as the additionalData:[] trap in tc-workflow-authoring: an empty array for an optional member is worse than no member.
    • The XSD does not list timestamp on PropInfo (1009 declares only object and vecNameVal), yet the JSON REST binding requires it. The XSD is a lead here, not the contract. Confirm against the tier.

    ★★ The response tells you it did nothing, if you read the raw envelope. A no-op returns ServiceData.updated naming a UserSession object rather than your target. A helper that reports "ok" on HTTP 200 hides this completely. Check that updated contains the uid you passed.

  4. ★★ removeChildren is Core-2014-10, not Core-2007-01, and needs clientId and propertyName. On the wrong version it returns 200 and the folder contents do not change.

  5. ★★ A folder-targeted create puts the REVISION in contents, not the item. So a removeChildren aimed at the item uid succeeds, reports nothing, and removes nothing, and the subsequent delete still fails as referenced.

  6. ★★ getFileReadTickets returns PARALLEL ARRAYS. tickets is [[ImanFile model objects], [ticket strings]]. Taking tickets[0] hands the downloader a list of model objects and gets zero bytes back, which reads exactly like an empty dataset. Lane 1 is the tickets.

  7. ★★ ref_list holds file-object uids, not file names. Checking whether the file name appears in ref_list compares a name against a uid and reports "content not verified" on datasets whose content uploaded perfectly. Round-trip the bytes.

  8. ★★★ A workflow template created and not captured is unreachable. See below.

  9. ★★★ A RELEASED revision silently refuses a dataset attachment. createDatasets takes container and relationType and returns the dataset either way. Measured here: the frozen criteria_registry.yaml was created with real, byte-verified content and TC_Attaches on the criteria set revision came back empty, because that revision had already been released to freeze it. The result was a real file hanging off nothing, and every check that inspected the dataset rather than the container said it had worked.

    Ordering consequence, and it is the important one: anything that must live on a frozen object has to be attached BEFORE the release. For a criteria set that is also the right order on its own terms, because the thing being frozen and the record of what was frozen should become immutable in the same act. Verify the attachment by reading the CONTAINER's relation property, never the dataset.

The workflow, and the trap that strands a template

createOrUpdateTemplatecreateOrUpdateHandler → publish works entirely over SOA, GUI-free. Proven live: Decision Review and Approval (ISO 15288 6.3.3), uid Qikp3ZCxZ$cfED, template_classification: 0 (Process), stage 2 (Online), with EPM-set-owning-project-to-task bound on the process root's Start action alongside the OOTB EPM-attach-related-objects, EPM-assign-team-selector and EPM-auto-assign-rest, all read back.

★★★ The uid is in createdorUpdatedObjects[0].templateObject.uid. It is NOT in ServiceData.created, which is null, and it cannot be recovered by name afterwards. Measured on cloud2506: getWorkflowTemplates (2013-05, all four required attributes, includeUnderConstruction: true) returned the same 402 templates before and after two successful creates, and zero templates at stage 1. Under-construction templates are invisible to that route on this tier regardless of the flag.

The consequence is nasty: a created-and-uncaptured template holds its name against a re-create (33120 "Root template name is already in use") while being unlistable, unpublishable and undeletable by name. It is stranded. Capture the uid from the create response and publish immediately, then it becomes findable.

★★ Publishing uses template_stage, not stage. The property is stage; the createOrUpdateTemplate key at the publish step is template_stage. They are different keys in the same map, and the wrong one returns 200 and changes nothing. Step 1 (create) genuinely does use stage.

What the template should NOT do: gate on the engine's verdict. A contested result is a legitimate thing to approve with the contest recorded, and a clean result is a legitimate thing to reject. A rule handler that auto-approves above some score moves the decision away from the person accountable for it, which is the whole thing clause 6.3.3 c) exists to prevent.

Active Workspace: register on your own type, and four silent render bugs

Scoping a tab belongs in the REGISTRATION, not in visibleWhen. The Customization book says so, and the tier agrees: a tab registered on one type and gated on a condition about a different type renders precisely nowhere.

Corrected 2026-08-09. This section previously said the only registration available is AWC_ItemRevision.SUMMARYRENDERING (tier wide, gated behind a site preference that ships false) because without a BMIDE template there is no decision type. That was true of the no-template design and it is no longer the recipe. Ship the type. The module registers on Dss4DecisionRevision, contends with nobody, patches no Siemens file, and uninstalls by deleting two preferences. If you find yourself gating a tab on a site preference to stop it appearing tier wide, the registration is in the wrong place.

AWC_<YourRevisionType>.SUMMARYRENDERING = <your host summary>
AWC_<YourItemType>.CREATERENDERING      = <your create panel>

Author the create panel too, not just the tabs. A module with summary tabs and no create panel gives a reviewer somewhere to look at a decision and no way to start one. That was raised as a review finding. Only properties on the create operation input list can appear on it: adding a property to the style sheet alone is not enough.

★★ Give every list an Add action and source it from a TYPED relation.

<command commandId="com.teamcenter.rac.common.AddNew" priority="200">
  <parameter name="visibleTabs" value="new,search"/>
</command>

A generic relation (FND_TraceLink, IMAN_reference) makes the Add picker offer every object on the tier, which was the other review finding. The typed relation is what constrains it. Read-only objectSets under paragraphs of explanatory prose attract "what is this tab for, it is so sparse" no matter how correct the data model beneath is.

Say what an empty list means, per list. It differs: no criteria is a gap to fill, no eliminated alternatives is normal, no assumptions is a finding. "No data" alone leaves the user unable to tell good from bad.

Four bugs that render as an empty panel rather than as an error

None produces a parse failure or a log line. All four were live in this module's files.

  1. ★★★ The display element is <tableDisplay>. <table> is not one, and defaultdisplay must name a display element, so defaultdisplay="table" is invalid too. Valid: tableDisplay, listDisplay, imageDisplay, thumbnailDisplay.
  2. ★★★ Check whether your types are Items before naming item_id. Sub-objects of a decision (criterion, alternative, rationale, assumption) are naturally WorkspaceObject subclasses, which have no item_id and no release_status_list. Those columns come back blank and sortby="item_id" sorts on nothing.
  3. ★★★ Read freeze evidence off the object that carries it. A criteria tab rendering on the DECISION revision that reads date_released from the page object shows when the decision was approved and labels it the criteria freeze. Source it from the criteria set instead. This is the one claim such a tab exists to make.
  4. ★★ A tab is worth one <property name="object_string"/> while commissioning. It separates "wrong registration" from "content not resolving" in one run.

Where the arithmetic lives

Hard vetoes, MAUT normalisation, the robustness pass and the sensitivity checks are part of the module, as versioned, tested code shipped alongside it.

Corrected 2026-08-09. This section previously argued the arithmetic should live in a separate repository so a decision could be reproduced by someone with no Teamcenter login. Review rejected that split: a systems engineering trade is run from Teamcenter, against Teamcenter data, so the capability belongs with the module rather than beside it. The reference implementation now ships in the module repo and is what pins the behaviour (the ordering, the input rules, the output shape) under test.

What genuinely stays out is the Teamcenter server. Do not re-implement the arithmetic as ITK or a BMIDE extension: that puts the arbiter's numbers somewhere no one can diff and no test can reach.

And do not grow a solver. Where a trade needs computation Teamcenter does not perform, call an existing Siemens product: Monte Carlo and design space exploration go to Simcenter HEEDS, with Teamcenter passing the trade space out and taking the results back. Writing your own is how a module acquires a numerical core nobody owns.

Teamcenter holds the inputs and the outcome as evidence. Check the record in as a dataset with real file content and relate it to the objects it decided about, plus the frozen registry, or the scores cannot be recomputed and the decision is an assertion.

Report a win frequency as a win frequency. The DSS schema has a confidenceLevel field and a Monte Carlo win frequency is not a confidence: it is the share of sampled weightings in which a candidate led. Putting one in the other's field is a category error that will be read as a probability of being right.

Definition of done

  • Every object re-fetched by uid after creation, never reported from a create response alone.
  • The decision record's bytes round-tripped through FMS and compared, not inferred from ref_list.
  • The pre-registration check run against real date_released and creation_date values read back off the tier.
  • Test litter swept by item id using the OOTB Item ID saved query. The Item Name query matches object_name, which is the wrong field for an id-prefix sweep and will report zero while the objects sit there. That exact miss happened here: a DM-PROBE- sweep through Item Name came back clean because the probe objects were named "probe A".
  • Delete order: cut folder membership first (Core-2014-10-DataManagement/removeChildren, on the revision uid), then delete, then re-fetch to confirm. Released objects need the DBA account.

Related skills

tc-object-authoring, tc-parameters-units, tc-relations-traceability, tc-datasets-files, tc-workflow-authoring, tc-awc-custom-tab, tc-awc-stylesheets, tc-verify-and-cleanup, tc-deployment-center, tc-mission-engineering (the other front-of-life-cycle gap that turned out to be a documentation gap rather than a product gap).

Implementation: se-process-skills/tcdm/ (module), se-process-skills/engine/ (aggregation), D:\repos\decisionsupport (the DSS prototype BMIDE template).


Generated from skills/tc-decision-management/SKILL.md in the tc-automation-skills library, which is the canonical copy and also serves as the agent skill set for Teamcenter work.