TeamcenterKnowledge

Skills

TC Classification Hierarchy

Skill tc-classification-hierarchy. Build the Teamcenter Classification TREE ITSELF - the class/hierarchy nodes, not the placements into them. Three ways in (the documented SOA op, Classification Manager's Add Class panel, its JSON/PLMXML Import), which one works when, the account trap whose error message reads like a wrong-data-model conclusion, the four HierarchyNodeDetails fields whose XSD prose is misleading, and why there is no safe undo. Use before any classification bulk-load when the target branch does not exist yet, and for designing a new taxonomy.

The companion to tc-classification-bulkload. That skill loads ICOs (placements) into classes that already exist. This one is about making the classes.

Domain-independent: the F-35 run built a Standard Notes tree, the next built a Signals taxonomy. The mechanics do not change with the domain; only the breadcrumb source does.

Do the mapping before you create anything

Design the tree against real items and map them to it on paper first. This is not ceremony: on the Signals run, mapping 16 live signals onto a draft taxonomy found two modelling defects before a single node existed - an item typed as a Signal that actually conveyed fuel (a mass flow, not information), and a command signal declared on the wrong interface. Neither was a gap in the taxonomy. Both were the taxonomy telling the truth about the model.

A node you create is effectively permanent (see "There is no undo"), so the cheap moment to be wrong is before the first create, not after nineteen.

Design guidance that has held up twice: if items are classified along two genuinely independent axes, make both real branches rather than nesting one inside the other, and let multiple classification (allowMultipleClassification: true) put an item in one class of each. Nesting forces re-classification when the subordinate axis changes even though the item's meaning did not.

Three ways in, and when each works

1. SOA: ClassificationCore-2013-05-Classification/createOrUpdateHierarchyNodes

The documented op, found by grepping the Help Server WSDL kit (ClassificationCore1305Classification.xsd), not by guessing service names - a first blind guess of ClassificationCore-2013-05-ClassificationCore/... used the wrong domain segment and faulted with a generic syntax error indistinguishable from "no such operation". The real domain segment is Classification, matching the XSD's own namespace.

{"hierarchyNodeDetails": [{
  "nodeToUpdate": NULLTAG,                 // NULLTAG = create mode
  "parent": {"uid": "<parent NODE_UID>", "type": "unknownType"},
  "masterNode": NULLTAG, "icon": NULLTAG, "images": [], "attachments": [],
  "nodeId": "<id>", "nodeName": "<name>", "nodeDesc": "<desc>",
  "nodeType": "HierarchyNode_Master",      // Group/Master/Reference; Group untested
  "storageClassTypeName": "",              // empty even for a Storage Class
  "isLeafNode": false, "levelSortIndex": -1,
  "isAbstract": true,                      // true = Abstract Class, false = Storage Class
  "isAssembly": false, "allowMultipleClassification": true, "instanceCount": 0
}]}

The parent's uid is the existing node's NODE_UID property, readable from findClassificationInfo3's clsClassDescriptors[...].properties - the same read used to resolve classes for placements, no extra lookup.

The new node's uid comes back in the top-level nodeObjects array (one per input entry, in order), not under ServiceData.created.

Four fields whose XSD prose misleads. All corrected by reading a real existing node back with getHierarchyNodeDetails rather than reasoning about the schema:

  • storageClassTypeName looks like a type name. A first guess of "Cls0ClassBase" faults with class Cls0ClassBase cannot be found (132008). A real Storage Class node's value is its own numeric class id - self-referential, and it does not exist until after creation. Leave it empty; it populates itself.
  • isLeafNode reads back false on a real, genuinely terminal Storage Class. It is not creation intent. Leave false.
  • levelSortIndex reads back -1, not a sibling ordinal. Do not compute sibling ordering.
  • isAbstract is the only field that actually distinguishes Abstract from Storage.

2. Classification Manager (Active Workspace) - the Add Class panel

Works as the logged-in user, which matters when the SOA route is refused (below).

Select the parent node in the tree, then New -> Add Child (or Add Sibling). The panel asks for, in order:

Field Values
Class Type Blank, Group, Abstract Class, Storage Class
ID required, must be unique
Name required
Parent shown read-only, from the tree selection
Unit System Blank, Metric, Non-Metric, Both

Add stays disabled until all required fields are set, and success shows Class "<name>" definition created. Verify from the Details panel (ID, Class Type, Is Abstract, parent) rather than the toast.

Cost: roughly six interactions per node. Fine for a handful, painful past a dozen.

3. Classification Manager Import - JSON or PLMXML

The Import button takes a JSON or PLMXML file through a native file picker. This is the one-shot route for a whole tree and the right answer for anything past a handful of nodes.

Two things to know before planning around it:

  • The picker is a native OS dialog. Browser automation cannot drive it; you need desktop control or a person to choose the file.
  • There is no Export to learn the schema from. The ... overflow menu's Import/Export submenu offers only "Import Vendor Hierarchy". Get the JSON/PLMXML schema from the Help Server kit, not by round-tripping an existing class.

★ The account trap, and the wrong conclusion it invites

Node creation needs a DBA-privileged account, the same "Active Admin" boundary as the Classification Manager UI. The documented symptom is a clean privilege fault, code 51057.

It does not always present that way. Running as a non-DBA service account against a tier whose tree was created by infodba, the identical request - same parent NODE_UID, same body, same tier - faults instead with:

7023  Given smlb0 when expecting N10Teamcenter14Classification21Cls0HierarchyNodeI

That reads as a data model error: "this tree is made of legacy SML classes, so the Cls0 operation does not apply here". Every class on the tier also reports CLASS_OBJECT_TYPE: SMLClass, which corroborates the wrong story beautifully. The conclusion is wrong. The same op, same parent, same tier had already worked under infodba.

Before concluding a tier's classification tree is the wrong model for this operation, re-run the identical call as a DBA. A type error is not always about types.

Placements: use the PUBLIC op, not the AW-internal one

The F-35 reference implementation writes placements with Internal-IcsAw-2018-12-Classification/saveClassificationObjects2, an undocumented AW-internal endpoint. Copying that is a trap worth naming, because there is a public, documented operation in the same service as the hierarchy op:

ClassificationCore-2013-05-Classification/createOrUpdateClassificationObjects
  classificationObjectInfos: [{
    objectId: "<id, create only>",
    objectToUpdate: NULLTAG,          // create mode
    nodeRef: {uid: "<CLASS NODE_UID>"},   // the node uid, NOT the class-id string
    classifiedObjectRef: {uid: "<item uid>"},
    propValues: [{name, values[]}]
  }]

Note nodeRef takes the class's NODE_UID (readable from findClassificationInfo3), not the CLASS_ID string that the AW-internal endpoint wants. That difference alone will send you in circles if you assume the two APIs are interchangeable.

The lesson underneath it: the local Help Server WSDL kit had this the whole time and was not consulted, because an existing script in the repo used the internal endpoint and that felt like precedent. A working script is evidence that something CAN work, not evidence that it is the supported path. Grep wsdls/*.xsd for the capability before copying a call shape from code -- see tc-soa-docs-navigation.

On saber2506, saveClassificationObjects2 refuses every create shape tried (asking isLoaded on NULLTAG, code 65, with clsObject NULLTAG and omitted; the 2019-12 version rejects the same body as invalid syntax), and the public op returns 214022 parse errors until the body matches the XSD exactly. After two or three failed shape guesses, stop guessing and capture what Active Workspace itself sends -- tc-capture-awc-calls exists for precisely this.

The plmxml plus plmxml_import route remains the officially supported one for bulk, and is the only one that rebuilds the search index.

★ Placements on a CLASSIC tree: the op that actually works

On a classic (SMLClass) tree, neither of the two obvious candidates is the answer:

op result on saber2506
Internal-IcsAw-2018-12.../saveClassificationObjects2 asking isLoaded on NULLTAG (65), every create shape
ClassificationCore-2013-05.../createOrUpdateClassificationObjects 214022 parse errors; wants a Cls0 node uid
Classification-2007-01-Classification/createClassificationObjects works

The classic-era op is the one that matches a classic tree, because it takes classId as the plain string the tree uses rather than a node uid:

{"clsObjs": [{
  "clsObjTag": NULLTAG,                       // create
  "wsoId": {"uid": "<item uid>"},             // the object being classified
  "properties": [],
  "instanceId": "", "classId": "SIGFCMD01",
  "unitBase": "METRIC"                        // enum METRIC/ENGLISH/UNSPECIFIED
}]}

unitBase is an enum in caps. Lowercase "metric" fails as An error occurred in DOM parsing (214007), which reads like a malformed body rather than a bad enum value.

Classification-2007-01 also has deleteClassificationObjects, so unlike hierarchy nodes, placements are reversible. That materially changes how much caution a placement run needs.

The prerequisite that produces a clear error

The type must be listed in the ICS_classifiable_types site preference or every create fails:

71188  In order to classify the Workspace Object "X", please ensure that its type
       "Seg0IntfSpec" is listed in the preference "ICS_classifiable_types".

That is an unusually good error message. Take it at face value; it is the whole blocker.

★★ Site preference writes SILENTLY NO-OP without privilege

Adding the type needs a DBA. Without one, on this tier, three different operations all report success and change nothing:

  • Administration-2012-09.../setPreferencesAtLocations
  • Administration-2012-09.../setPreferences2
  • Administration-2007-06.../setPreferences

All returned no partial errors. A getPreferences read straight afterwards showed the value completely unchanged. There is no fault, no privilege code, no warning.

Always read the preference back after writing it. "No partial errors" from a preference write is not evidence of anything, and reporting it as done is exactly the mistake this note exists to prevent.

The search index makes "already exists" unreliable

Same caveat as REST placement writes. A node created by any of the three routes is invisible to findClassificationInfo3 until smlutility -create_indexing_views rebuilds the index. Confirmed live: querying a just-created node by name returned zero matches while a direct getProperties on its uid confirmed it fully exists.

Consequences:

  • Within a run, track new nodes from the create call's own nodeObjects response. Never re-query to "confirm" a node you just made.
  • Across runs, an existence check can wrongly report a previously-created node as missing, so a re-run risks duplicate creates. Get smlutility -create_indexing_views run after an --apply before trusting idempotency.
  • Verify with getProperties on the returned uid. That bypasses the index and is the only read that answers honestly straight after a create.

There is no undo

Core-2006-03-DataManagement/deleteObjects does not remove a Cls0MasterNode: the call returns no error and the object is still fully readable afterwards. No dedicated removal op has been found.

So:

  • Name anything experimental unmistakably (*_DELETE_ME_*) and clean it up through the Classification Manager UI.
  • Get the ID convention right the first time. Match the site's existing style rather than inventing one: on a tier whose fixed roots read ENGICM01 / STDPRTICM01, a new branch under Engineering follows as SIGICM01, SIGFNICM01, and so on. Deeper custom classes on the same tier use opaque numeric ids, so both conventions coexist and either is defensible - but you only get one shot.
  • Create top-down. A child needs its parent's uid, so a failed parent blocks everything under it; report those as blocked_on_parent rather than attempting the child.

Abstract or Storage

Heuristic that matches every existing tree observed: a node that is a prefix of a longer path is organizational (Abstract Class); a true leaf holds ICOs (Storage Class). Groups sit above both and are what the fixed roots (SAM, ICM, ENGICM01) are.

Only Abstract and Group variants have been live-tested via SOA. Storage Class has been created through the UI.

Reference implementation

Siemens/f35-notes-classification/build_hierarchy_plmxml.py - reads a breadcrumb spreadsheet, diffs against live, creates only what is missing, --apply gated, writes a report. Shares tc_client.py with the placement stage. Fork it and change only the sheet name, the column, the path separator and the root id.

Related

tc-classification-bulkload (the placements that go into this tree), tc-soa-docs-navigation (how the op was found - grep every .xsd), tc-soa-payload-shapes (NULLTAG, the full-struct rule), tc-verify-and-cleanup (never report a node a call did not return).


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