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_listand 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.
★★★
createRelationsreports NO created uid for relations it created. The new relation is inoutput[0].relation.uid;ServiceData.createdcomes back empty. Readingcreatedand 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.★★★ A non-DBA account's
deleteObjectshides its partialErrors. As an ordinary user it returned a bare ServiceData with nodeletedlist, nopartialErrorsand no fault, having deleted nothing. The same call on the same objects as the DBA account returneddeleted: 19and, 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.★★★
setPropertiestakesinfo/vecNameVal, NOTobjects/attributes.⚠ CORRECTED 2026-08-09, and the earlier version of this entry was wrong. It said the fix was to read
lsdoff the object and pass it as a top-leveltimestamp. 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/setPropertiestakesinfo, a list ofPropInfo. A body built fromobjectsandattributescontains noinfo, 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:
- ★★
timestampgoes INSIDE eachinfoentry, not at top level, and an empty string is fine. A top-leveltimestampis not a member ofSetPropertiesInputand faults the JSON parser. You do not needlsd. - ★★ Omit
optionsentirely."options":[]and"options":[""]both fault the JSON parser. Same shape as theadditionalData:[]trap intc-workflow-authoring: an empty array for an optional member is worse than no member. - ★ The XSD does not list
timestamponPropInfo(1009 declares onlyobjectandvecNameVal), 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.updatednaming aUserSessionobject rather than your target. A helper that reports "ok" on HTTP 200 hides this completely. Check thatupdatedcontains the uid you passed.- ★★
★★
removeChildrenisCore-2014-10, notCore-2007-01, and needsclientIdandpropertyName. On the wrong version it returns 200 and the folder contents do not change.★★ A folder-targeted create puts the REVISION in
contents, not the item. So aremoveChildrenaimed at the item uid succeeds, reports nothing, and removes nothing, and the subsequent delete still fails as referenced.★★
getFileReadTicketsreturns PARALLEL ARRAYS.ticketsis[[ImanFile model objects], [ticket strings]]. Takingtickets[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.★★
ref_listholds file-object uids, not file names. Checking whether the file name appears inref_listcompares a name against a uid and reports "content not verified" on datasets whose content uploaded perfectly. Round-trip the bytes.★★★ A workflow template created and not captured is unreachable. See below.
★★★ A RELEASED revision silently refuses a dataset attachment.
createDatasetstakescontainerandrelationTypeand returns the dataset either way. Measured here: the frozencriteria_registry.yamlwas created with real, byte-verified content andTC_Attacheson 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
createOrUpdateTemplate → createOrUpdateHandler → 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.
- ★★★ The display element is
<tableDisplay>.<table>is not one, anddefaultdisplaymust name a display element, sodefaultdisplay="table"is invalid too. Valid:tableDisplay,listDisplay,imageDisplay,thumbnailDisplay. - ★★★ Check whether your types are Items before naming
item_id. Sub-objects of a decision (criterion, alternative, rationale, assumption) are naturallyWorkspaceObjectsubclasses, which have noitem_idand norelease_status_list. Those columns come back blank andsortby="item_id"sorts on nothing. - ★★★ Read freeze evidence off the object that carries it. A criteria tab rendering
on the DECISION revision that reads
date_releasedfrom 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. - ★★ 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_releasedandcreation_datevalues read back off the tier. - Test litter swept by item id using the OOTB
Item IDsaved query. TheItem Namequery matchesobject_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: aDM-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.