THE GATE

The Port Coverage Gate

A deterministic transform will hand you a wrong answer just as confidently as a right one. This is the layer that checks the answer: four independent questions asked of every run, each of which exists because the pipeline once failed silently in exactly that way and nobody noticed. It currently proves all 123 ports in scope land correctly, which a day ago it could not say of a single one.

Why a gate at all

Three separate defects reached a green pipeline on this deployment, and not one of them produced an error. A Teamcenter utility accepted a misspelled option, printed *** Importing File ***, created nothing and exited 0. A transform accepted a flag it did not support, ignored it and exited 0. A deduplication step discarded 43 real component instances and exited 0.

On this stack, "no error" is not evidence. Every check below exists because something passed while being wrong.

The four questions

They run in this order deliberately: each is meaningless if the one before it is untrue.

#QuestionAnswered againstFails on
1Conservation. Does the artifact still contain what the model had?the Teamcenter extractany component that vanished
2Coverage. Did every port land on something that can carry it?the neutral exchange bundleunrealized port/branch cells
3Regression. Does this run owe anything it did not owe before?the stored baselinenew gaps only, not pre-existing debt
4Provenance. Was the baseline produced by this same build?a hash of the transform coderefuses to compare rather than inventing a delta

Check the output against the source, not against yourself

This is the one that matters most, and it was the last to be built. A translator that reconciles its final artifact against its own intermediate is grading its own homework: both come from the same transform, so anything the transform drops is missing from both sides of the comparison.

That is not theoretical. Closing a version skew on two supporting modules took the component count from 93 to 50, discarding two of every three thrusters on a satellite model. The coverage ledger went from 213 subjects to 110 and reported every one of them blocking, exactly as before. A smaller number read as a smaller model, not a broken one. The only visible signal was the file shrinking from 1,094,386 bytes to 797,101.

So conservation starts from tree.json, the extract, which the transform did not write, and follows the counts forward one hop at a time:

  extract.json  -->  exchange bundle  -->  capital.xml
  (Teamcenter)       (neutral)            (Capital)

Checking each hop separately is what makes it useful. "93 became 50" is a bug report. "93 survived into the bundle and 43 vanished at the emitter" names the file to open.

93
extracted from TC
93
in the bundle
93
devices in Capital
0
dropped

Current run: every block occurrence Teamcenter reported reached Capital.

Count identities, never names The extract carries 93 block occurrences at 93 distinct occurrence paths under only 69 names, because the model legitimately holds three Thrusters, three Heaters, three Thermistors, three Antennas, three Valves and three watchdogs. A check that counted names would have agreed with the bug and reported everything fine. The gap between paths and names is printed on every run, whether or not anything was lost, because it is precisely the trap.
"Explained" is not "correct" The deduplication step recorded exactly how many components it merged, in a field that had been populated the whole time and that nothing ever read. The gate therefore refuses a reduction whether or not the transform claims it was deliberate. A contract of "nothing may vanish" catches this; "nothing may vanish unexplained" does not, and that distinction is the entire failure.

The port dashboard

Live figures from the current run of model 040808, read back out of Teamcenter rather than off a workstation. Every port in scope is now shown to land on something that can carry it.

123
cells in scope
123
proven realized
0
open findings
0
content lost
REALIZED
123
every port bound to an object that can carry it, proven per port by its Teamcenter id

123 cells, 123 device pins, 74 clusters, and nothing lost between the Teamcenter extract and the Capital document. This is the first run in which the gate has passed rather than reported.

The last 10 were a registry that existed twice Until an hour before this run, ten ports on the imagery path reached a cluster interface and stopped there with no device pin. The emitter kept a private copy of the domain token table, "so the emitter does not depend on the audit's registry", and it had drifted: the shared registry gained image and video as signal tokens when an HDMI interface was declared, and the copy never did. The auditor read imageOut as a signal port and expected a pin; the emitter read the same port as carrying no domain and wrote none. Consolidating onto the one registry closed all ten at once. The independence being protected was not real: the emitter already imported its sibling module, and the registry imports nothing at all.
A third of the findings were the gate's own bug The previous run of this page reported 47 findings, 29 of them UNREALIZED. They were false. The auditor indexed the Capital document by cluster name, and 93 clusters share only 50 names, so cells belonging to a duplicate namesake were judged against a different component's interfaces. The tell was in the distribution: all 29 sat on duplicate-named components, while the artifact demonstrably carried all 138 port ids. Keying on the port id instead moved 27 of them to REALIZED and 2 to DEGRADED, and UNREALIZED is now zero.
Where this started 213 cells, 213 blocking, every one either UNVERIFIABLE or NO_ARTIFACT. A gate that fails everything carries exactly as much information as one that passes everything. Three changes moved it from a number to a measurement, and not one of them was a change to the model.

213 was never 213 findings

The old headline decomposed into two things that had nothing to do with each other, and neither was a gap in the model.

90 cells were our own misconfiguration. NO_ARTIFACT against the software, NX and Amesim branches: this pipeline builds the Capital artifact and nothing else, so those cells said only "this pipeline does not build software". The auditor had supported a --branches scope since it was written, and its own source warns that an unscoped run "buries the real finding under noise". The tier simply never passed it. Scoping to capital removed 42% of the output at a stroke.

123 cells were one defect, not 123. Every one carried the identical evidence string, and every one was routed: true:

{ "component": "Flight Processor",
  "port": "attDataIn", "domain": "signal", "branch": "capital",
  "state": "UNVERIFIABLE", "routed": true,
  "evidence": { "interfaces": 2,
    "why": "cluster interfaces are named IF1..IFn with no
            Teamcenter port name to bind against" } }

One evidence string repeated 123 times is a single fix wearing a crowd's clothing. It is worth reading the evidence before counting the rows.

Port identity: the thing that made the number mean something

The diagnosis was verified directly against the 1.1 MB artifact rather than taken from the auditor's own claim. The file carried 145 clusterinterface elements and exactly six distinct names between them, numbered positionally, one sequence per cluster. shortdescription was empty, so the Teamcenter name was not parked there either, and the string attDataIn appeared zero times in the whole document.

Connectivity survived; identity did not. The pathways were right, the 43 topological segments were right, every port was routed. What could not be established was which Teamcenter port any given Capital interface corresponded to.

BeforeNow
distinct interface names633
what they look likeIF1, IF1, IF1 …attDataIn, busDataIf, monDataIn, pwrIn, imageOut
TC Port ID properties0251
components in the artifact9393 (50 had the collapse come with it)
coverage verdict123 capital cells, not one of them verifiable123 cells, all 123 realized
Why this mattered more than a cosmetic naming gap Nothing downstream could detect a mis-assignment. If the emitter ever bound a cluster's interfaces in the wrong order, every check would still have passed: the ports were all present, all routed, all counted. Adversarial testing against a blind model corpus has produced exactly that failure once already, as non-unique port ids silently mis-wiring. The name is for a human reading the diagram; the fixed TC Port ID property is the join key, deliberately read-only so an engineer can rename an interface for legibility without breaking reconciliation.

Why this took longer than turning on a flag

Both fixes were configuration, not new code. Neither was a one-line change, and the reason is worth recording.

The flag already existed and had been deliberately left off. --port-identity was built and tested long before this, switched off because it changes the emitted Capital contract and that is the caller's decision. But the transform on the Dispatcher host predated the flag entirely. An older build does not reject an option it has never heard of: it accepts it, ignores it, and exits 0. Passing the flag would have changed nothing and reported success, which is the same silent no-op that let a stale transform swallow --emit-exchange for weeks. The module now checks the artifact really contains a TC Port ID when identity was requested, and says so loudly when it does not.

And the newer transform brought a defect with it. The emitter that knows how to stamp port identity also collapses components sharing a name, unconditionally, and on this model that discards 43 of 93 occurrences including two of the satellite's three thrusters. The two features shipped in the same generation of code, so port identity could not be adopted without the loss. That collapse is now an option rather than a certainty, defaulting to the long-standing behaviour so nothing else moves, with an explicit opt-out taken only where it has been measured.

The conservation check earned its keep here Enabling port identity meant deploying a transform already known to lose content. The counts held at 93 extracted, 93 in the bundle, 93 devices in Capital, nothing dropped. Without that check the deployment would have been a coin flip settled by reading file sizes.

What the gate refuses to say

Two deliberate silences, both of which cost more to build than the loud version would have.

A missing baseline is reported as could not compare, never as "no changes". Those look identical on a dashboard and mean opposite things. On a first run the tool says plainly that it had nothing to compare against.

A baseline produced by a different build of the transform is refused outright. When the pipeline changed underneath it, comparing across that boundary would have manufactured roughly a hundred phantom removals, addressed to engineers who caused none of them and could fix none of them. The baseline now carries a fingerprint of the transform code, and a mismatch produces:

BASELINE  was produced by a different pipeline build
            baseline ceb9cf9cc72830cd   this run a8f82005d66655e7
            No comparison was made. A transform change is not a model
            change, and diffing across one reports gaps nobody
            introduced and nobody can fix.

That guard was written for a hypothetical and earned its keep the same day, firing correctly on a real rollback.

Running it

Both checks run inside the Dispatcher module as ordinary stages, and both are advisory until switched on. The switches are separate on purpose: a coverage regression is debt, while a conservation loss is a wrong artifact, and a site may reasonably block on the second long before it can afford to block on the first.

$ node bin/audit-extract-conservation.mjs \
      --tree tree.json --exchange exchange.json --capital capital.xml --gate

  extract   block occurrences            93
            distinct occurrence path     93
            distinct name                69   <- NOT an identity
  exchange  components                   93
            reported as merged            0
  capital   devices                      93

  PASS - every block occurrence Teamcenter reported reached Capital.

The run also emits a deliberately tiny rollup.json, flat enough that a Teamcenter saved query can turn it into a native report without parsing anything:

{ "model": "040808", "coverage_scope": "capital",
  "coverage_subjects": 123, "coverage_blocking": 0,
  "coverage_regressions": null, "coverage_compared": false,
  "extract_blocks": 93, "emitted_components": 93,
  "dropped_components": 0, "conservation_losses": 0 }

extract_blocks and emitted_components both read 93 while only 74 components reach the Capital document, and that is not a contradiction: Capital is now told to carry only components with an electrical or signal port, so a valve, a tank, a thermistor and a radiator are held back by declaration. The conservation check is given the same restriction the emitter was given, and reports it as declared rather than as content going missing. Without that, a deliberate scope and a silent drop would look identical at this hop.

Those 7 regressions are not real either, and that is a fourth lesson They are the auditor fix landing: 27 cells moved UNREALIZED → REALIZED and 2 moved UNREALIZED → DEGRADED, and the diff counted the downgrades and the newly-visible cells as new debt. The provenance check should have refused this comparison and did not, because it hashed the transform and not the auditor — and the auditor is what computes the ledger. A phantom delta walked in through the one door the guard had left open. The fingerprint now covers the measurer as well as the thing measured.

The Dispatcher wiring these stages sit inside is documented on the Capital branch page, and the gaps this gate has surfaced are tracked on the status board.

What each state means

The report states facts and links here. These are the verdicts the auditor can record for one port on one branch, worst first. The code in the second line is the machine name written into the ledger and used by waivers; it is what to quote in a bug report.

StateWhat it assertsBlocks a handoff
Wrong discipline
MISBOUND
The branch bound this port to an attach point whose discipline contradicts the port domain.Yes
Outside branch scope
OVERCLAIMED
The branch materialized this port although its domain is outside what the branch can represent.Yes
Fallback attach point
DEGRADED
The branch bound this port to a fallback attach point rather than a declared port of the symbol.Yes
Not bound
UNREALIZED
The branch ran and holds the component, but never bound this port to anything.Yes
No artifact produced
NO_ARTIFACT
This port routes to a branch that has produced no artifact at all.Yes
Not routed here
UNDECLARED_ROUTE
The branch holds this port and can carry it, but the routing registry never sent it there.Yes
Identity not carried
UNVERIFIABLE
The branch materialized objects but carries no port identity, so binding cannot be checked either way.Yes
No recognised domain
UNROUTED
This port carries no recognisable domain, so no branch is expected to hold it.Yes
Emitted more than once
DUPLICATED
The branch holds this port more than once, every copy carrying the same identity.Yes
Unwired in Teamcenter
UNWIRED_IN_SOURCE
Teamcenter itself wires this port to nothing, so no branch can bind it.No
Waived
WAIVED
A blocking state that a named person has accepted on the record.No
Realized
REALIZED
Bound to a declared attach point that can carry this port domain.No

Only one state is recorded per port/branch cell. Where several apply, the worst is reported, so a port that is both bound to the wrong discipline and using a fallback attach point is reported as Wrong discipline.

Corrective actions

Each state is addressed to somebody specific. A finding routed to the wrong owner is the main reason these sit unresolved.

StateWhat to do, and who does it
Wrong disciplineBranch owner: rebind to an attach point of the correct discipline. This is a wiring defect, not a gap.
Outside branch scopeBranch owner: stop emitting it, or correct the domain routing so it is not sent here.
Fallback attach pointSymbol owner: declare a real port on the symbol, then rebind.
Not boundBranch owner: bind the port. The component arrived, so the gap is in the port pass.
No artifact producedRun that branch, or scope the audit with --branches to the branches this pipeline actually builds. An unscoped run reports every branch you did not build.
Not routed hereRegistry owner: add the domain to the routing registry.
Identity not carriedBranch owner: carry the port identity through export. Waivable per branch by a named owner while that work is outstanding.
No recognised domainModel owner: type the port, or add its domain to the registry.
Emitted more than onceBranch owner: de-duplicate the export. One port, one pin.
Unwired in TeamcenterModel owner, not the branch. This does not block a handoff, because no branch can clear it.
WaivedReview at the waiver expiry. The debt is visible, not cleared.

Two findings are commonly misread as model defects and are not. No artifact produced usually means the audit was run unscoped against branches this pipeline never builds; scope it with --branches. Unwired in Teamcenter is addressed to whoever owns the model and deliberately does not block, because no branch owner can clear it.