Skills
TC BOM Structure
Skill
tc-bom-structure. Read and build Teamcenter product structure over SOA. The working three-call sequence for BOM windows and occurrence trees (createOrReConfigureBOMWindows, expandPSAllLevels, connector endpoint properties), the proven occurrence child-add write, and why an empty 200 here means wrong request shape before it means deprecation. Use for BOM, occurrence, connectivity or structure work.
This is the area where 200 OK with an empty result has cost the most time. In
this module that signature has meant wrong request shape more often than a
dead operation, so rebuild the payload from the XSD before you go hunting for a
successor. Read tc-soa-payload-shapes rule 2 for the deprecation case, and the
correction at the bottom of this file for why it is the second thing to check
here, not the first.
Reading structure: the proven three-call sequence
1. Open a BOM window
Cad-2019-06-StructureManagement/createOrReConfigureBOMWindows, struct
CreateWindowsInfo3.
Prefer the 2019-06 operation: Cad-2007-01-StructureManagement/createBOMWindows
is genuinely deprecated (TC 12.2, per the @Deprecated annotation), so 2019-06 is
the right call on style grounds.
But this skill used to say 2007-01 was a dead silent no-op, and that was false. Retracted 2026-07-31, disproven live. Both bodies were sent to the same tier in one script, minutes apart:
| Request | Result |
|---|---|
{"input":[{"itemRev":{...}}]} (the body the no-op finding was based on) |
200 OK, output: [] |
correct {"info":[{...6 members + clientId...}]} |
real BOMWindow + BOMLine |
The deprecated operation works. The original body had the wrong top-level
array name (input instead of info) and was missing five of six required
members plus both clientIds, so it could never have succeeded regardless of
deprecation. It never tested deprecation at all.
Three earlier records had already said so, each stressing shape, and none was
reconciled with the later "it is dead" write-up: harness-studio (2026-07-17,
"createBOMWindows on RequirementSpec WORKS"), the plm-teamcenter agent profile
("works but REQUIRES the full info shape"), and capital-tc-integration
("REQUIRES full info shape incl revRuleConfigInfo/overrideFolders").
CreateBOMWindowsInfo (2007-01) requires item, itemRev, bomView,
revRuleConfigInfo, objectForConfigure (singular, unlike 2019-06's
objectsForConfigure) and activeAssemblyArrangement, plus clientId on the
outer struct and the nested revRuleConfigInfo. Both unitNo: -1 / date: ""
and unitNo: 0 / date: "0001-01-01T00:00:00Z" were verified to work.
CreateWindowsInfo3 is a strict sequence in which bomWindow, item, itemRev,
bomView, revRuleConfigInfo, activeAssemblyArrangement and configContext are
all required. Every inapplicable one must be sent as NULLTAG
({"uid":"AAAAAAAAAAAAAA","type":"unknownType"}), never omitted, never null.
Nested RevisionRuleEntryProps additionally requires the date attribute even
when today: true.
The top-level array is info, not input, and clientId is required on BOTH
CreateWindowsInfo3 and its nested revRuleConfigInfo. Getting either wrong
returns 200 OK with output: [] and no error, which is indistinguishable from a
dead operation. This was hit and fixed live; see the correction below.
{"info":[{"clientId":"w1",
"bomWindow":NULLTAG, "item":{"uid":"<Item>"}, "itemRev":{"uid":"<ItemRevision>"},
"bomView":NULLTAG,
"revRuleConfigInfo":{"clientId":"w1","revRule":NULLTAG,
"props":{"unitNo":0,"date":"0001-01-01T00:00:00Z","today":true,
"endItem":NULLTAG,"endItemRevision":NULLTAG,"overrideFolders":[]}},
"objectsForConfigure":[], "activeAssemblyArrangement":NULLTAG,
"configContext":NULLTAG, "effGrpRevList":[], "bomWinPropFlagMap":{}}]}
Returns output[0].bomWindow (the BOMWindow) and output[0].bomLine (the top
line you expand from).
Print the exact member list any time:
python scripts/soa_lookup.py createOrReConfigureBOMWindows
2. Expand the occurrence tree
Cad-2007-01-StructureManagement/expandPSAllLevels
{"input":{"parentBomLines":[{"uid":"<BOMLine uid>","type":"BOMLine"}],
"excludeFilter":"None"},
"pref":{"info":[],"expItemRev":false}}
On item 041390 this returned 26 named Fnd0LogicConnRevision connector objects
matching exactly the item-id range visible in the rich client, over plain SOA REST.
Reading the response: parent and each entry of children are WRAPPER structs,
not ModelObjects. The uid lives one level down at .bomLine.uid:
output[i].parent.bomLine.uid <- NOT output[i].parent.uid
output[i].children[j].bomLine.uid <- NOT output[i].children[j].uid
output is flat: one entry per node in the whole subtree, each listing that
node's direct children. To get the direct children of the line you asked for,
match output[i].parent.bomLine.uid against your own top-line uid rather than
assuming output[0] is it. Reading .uid at the top level silently yields
None for every row, which looks exactly like an empty structure.
The expand's modelObjects do not carry bl_line_name / bl_item_item_id.
Follow with getProperties on the BOMLine uids to name the lines.
BOMLine uids (BOM::nnnnnn) are per-window and per-session. They change every
time you reopen, so never cache them across calls.
3. Resolve connector endpoints
expandPSAllLevels gives connector NODES but not which two ports each joins.
Item-level GRM on the connector's Fnd0LogicConnRevision carries no endpoint data
either (only Seg0Implements to the carrier and IMAN_master_form).
The answer is on the connector's occurrence (its BOMLine uid from the expand,
not its item revision). Port occurrences are type GDELine (base BOMLine), and
carry two plain properties:
fnd0bl_connected_end1 -> the BOMLine uid of the port at one end
fnd0bl_connected_end2 -> the BOMLine uid of the port at the other end
bl_connected_to_absName -> the full human-readable path for both ends
One getProperties call per connector. Confirmed on connector 041482:
Comms/Modem.dataIf.1 to Comms/Transceiver.dataIn.1.
Found by running getTypeDescriptions2 on GDELine rather than by guessing.
A real negative, for contrast
Mdconnectivity-2014-10-MDConnectivity/findConnectionsOnPort looked exactly right
by name and struct shape, but its core type Mdc0ConnectionElement does not exist
on this deployment per getTypeDescriptions2. That module simply is not installed.
That is a genuine "not on this tier" result, established with a type check rather
than a fault code.
Writing structure: occurrence child-add WORKS
Bom-2025-06-StructureManagement/addOrUpdateChildrenToParentLine
Proven live on TC 2506 (2026-07-31): added a child to a real assembly and
confirmed it by re-expanding, direct children 5 -> 6 and tree 22 -> 23 nodes.
The response returns the new BOMLine in itemLines[0].bomline.
{"inputs":[{
"parentLine":{"uid":"<top BOMLine uid>","type":"BOMLine"},
"viewType":"",
"items":[{"clientId":"add1",
"occType":"",
"usageRevisionType":"",
"preAllocatedPsOccThreadUid":"",
"item":{"uid":"<child Item uid>"},
"itemRev":{"uid":"<child ItemRevision uid>"},
"bomline":{"uid":"AAAAAAAAAAAAAA","type":"unknownType"},
"itemLineProperties":{}}],
"itemElements":[]}]}
usageRevisionType and preAllocatedPsOccThreadUid are the two required
attributes the 2025-06 struct adds over the older one. bomline is NULLTAG when
adding (it identifies an existing line when updating).
Full round trip verified end to end: add -> saveBOMWindows -> closeBOMWindows
-> fresh session sees the line -> removeChildrenFromParentLine -> save -> fresh
session back to the original 5 children.
Bom-2008-06-StructureManagement/removeChildrenFromParentLine
{"bomlines":[{"uid":"<BOMLine uid>","type":"BOMLine"}]}
Cad-2008-06-StructureManagement/saveBOMWindows
Cad-2007-01-StructureManagement/closeBOMWindows
{"bomWindows":[{"uid":"<BOMWindow uid>","type":"BOMWindow"}]}
You cannot verify BOM persistence by reopening in a new session
This is the trap that matters most here, and it produced a wrong finding earlier in this very investigation.
An add that has not been saved still shows up when you open a brand-new BOM
window in a brand-new login. It even survives closeBOMWindows without a save.
The obvious inference, that the write auto-committed, is wrong: those unsaved
changes live in the pooled tcserver process, and a later login that lands on
the same pooled server sees the modified BVR as though it were real. Three such
"persisted" lines vanished when the server-manager pool was restarted for
maintenance, which is what exposed the illusion.
So:
saveBOMWindowsis what commits. Unsaved is uncommitted no matter how convincingly a re-open shows otherwise.- A re-open in a new session is not proof of durability, only proof the change is in the pool. There is no cheap client-side way to distinguish the two, so save explicitly and do not reason from the reopen.
- Always
closeBOMWindows. Leaked pooled windows accumulate, and enough of them will exhaust the pool (symptom:routing: Proxyrequest failed to connect, then hard TCP timeouts). They can also hold locks on items, blocking deletes until the pool recycles.
Correction: the old "adds nothing" symptom was request shape, not deprecation
This operation was recorded for months as "parses but silently adds nothing," and that was later attributed to deprecation because the Services Reference names a 2025-06 successor. That attribution was wrong. The deprecation is real, but it is not what produced the empty result.
A malformed request produces a byte-for-byte identical signature to a dead
operation: 200 OK, empty result array, no fault, no message. That was
reproduced live in this same session on createOrReConfigureBOMWindows (see the
info vs input trap above), where correcting only the request shape turned a
permanently empty output: [] into a working BOMWindow.
This mistake has now been made at least three times in this codebase, which is why it is worth a section rather than a footnote:
| Operation | Blamed on | Actual cause | Status |
|---|---|---|---|
Bom-2008-06 addOrUpdateChildrenToParentLine |
deprecation | request shape (2 missing attributes) | write proven live |
Cad-2007-01 createBOMWindows |
deprecation | request shape (one-field body, wrong array name) | disproven live, deprecated op works |
createSchedule |
"Schedule Manager dead over REST" | unset SiteTimeZone site preference |
fixed |
In every case a 200 OK with an empty body was read as "this capability does not
exist here," and in every case the capability existed. The failure mode is not
ignorance of the API, it is treating an empty success as a diagnosis.
The createBOMWindows entry is the clean experiment: malformed body and correct
body sent to the same tier minutes apart, output: [] versus a real BOMWindow.
Deprecation did not stop it from working. So "the Services Reference says
deprecated" is not on its own a reason to expect an empty result.
So when a structure call returns 200 OK with nothing in it, the order of
investigation is:
- Rebuild the payload from the XSD sequence first. Every required member
present, correct top-level array name,
clientIdat every level that declares it, NULLTAG rather than omission ornull. - Check site preferences and session context (project/program, timezone).
- Only then suspect deprecation.
Shape is the cheapest hypothesis and has been the correct one every time so far.
tc-soa-payload-shapes rule 2 still holds for genuinely dead operations, but an
empty 200 is not by itself evidence of one. Before recording any operation as a
dead no-op, paste the exact request body into the finding. Every one of the
misdiagnoses above would have been caught on sight if the body had been written
down next to the conclusion.
createOrUpdateRelativeStructure faults, and this one is real
Run live 2026-07-31, both versions, against the same parent/child that
addOrUpdateChildrenToParentLine handles fine:
Cad-2007-01-...->214022JSON parsing.Cad-2025-06-...->214007 "An error occurred in DOM parsing", a gateway-level marshalling failure.
Five shape variants of the 2025-06 call (lastModifiedOfBVR empty vs ISO date,
bomViewTypeName "view" vs empty, with and without moveInfo) all returned the
identical 214007. Invariance across value changes points at the struct itself,
probably the cross-namespace imp2:MoveInfo / OccThreadEquivalent nesting, not
at anything callable from the client side.
This is a genuine fault, not the misdiagnosis pattern: it is a hard HTTP 500
with a fault code, not a 200 OK with an empty body. Unresolved, and not worth
more effort, because addOrUpdateChildrenToParentLine does the job.
Other structure operations: saveBOMWindows is Cad-2008-06.
The alternative that already works
For functional and logical decomposition, the Seg0Realize relation is proven,
semantically correct, and needs no BOM window at all. BOM is for physical product
structure. A root Fnd0LogicalBlock item named <PROJECT>-LBOM with component
occurrences is the proven logical-structure container pattern. See
tc-relations-traceability.
Requirement spec structure is occurrence structure too
Placing a requirement INTO a spec (so it shows as a numbered line rather than a
free-floating item) is
Internal-ActiveWorkspaceBom-2025-06-OccurrenceManagement/addObject5, with the
spec's occurrence BOMLine as parentElement (type Arm0RequirementSpecElement),
objectsToAddList[].objectToAdd set to the requirement Revision uid, and
productContext set to the spec's Awb0ProductContextInfo BOMLine. Those
SR::N::... uids come from the AWC URL's c_uid/pci_uid or from a spec-structure
expand.
That operation is not in the SDK's WSDL kit (it is AWC-internal). It was
captured verbatim from the AWC's own network traffic and has not been independently
replayed through a raw SOA client. Treat it as documented, not proven. See
tc-capture-awc-calls.
★ Reading logical blocks + ports out of expandPSAllLevels
Three things about the returned rows will each hand you a clean-looking zero or a double-count instead of an error. Verified live on item 041908 (2026-08-05).
1. Blocks and ports are typed on DIFFERENT fields. Match both or you get nothing:
ports -> itemType "Logical Port", underlyingType null (no underlying Item; class GDELine)
blocks -> itemType null, underlyingType "Fnd0LogicalBlockRevision"
Filtering blocks on itemType alone returns 0 rows and no error.
2. Rows are OCCURRENCES, so dedupe by object. 041908 returns 23 block occurrences
for 12 block objects and 58 port occurrences for 29 port objects — the same object
used under more than one parent. Collect by bl_line_object, but keep a
bomLineUid -> objectUid index as well, because connector endpoints
(fnd0bl_connected_end1/end2) are occurrence uids and will not resolve against
object uids. Occurrence names also carry an instance suffix (fuelOut.1); the
object's object_name does not.
3. Function pins are also "Logical Port". A port's parent tells you which
architecture it belongs to — Fnd0LogicalBlockRevision (logical) vs
FunctionalityRevision (functional). On 041908 that is 44 vs 14 occurrences. Without
the parent check, function pins look like untyped block ports and domain coverage
reads 22/29 instead of the true 22/22.
Port domain itself is not a property on the port: it is
port -> Seg0Implements -> Seg0Interface, then interface name -> your own registry.
Related skills
tc-soa-payload-shapes, tc-soa-docs-navigation, tc-capture-awc-calls,
tc-relations-traceability.
Generated from skills/tc-bom-structure/SKILL.md in the tc-automation-skills library, which is the canonical copy and also serves as the agent skill set for Teamcenter work.