fix: address 2nd-round adversarial review of PR #402 fixes

- core: decouple drag-follow from distributionRole — add
  portConnectivityFollow flag to NodeDefinition; pipe-trap opts out
  (portConnectivityFollow: false) so dragging a connected pipe endpoint
  stretches the trap arm instead of yanking the anchored trap fixture

- core: remove the module-level getLevelHeight cache entirely — it was
  keyed only by nodes-object identity, which could return stale heights
  for in-place mutations by pure/headless callers. The function is now
  fully pure and deterministic; viewer hot path recomputes per frame as
  before (the cache only ever skipped the resolver-free branch)

- test: harden port-connectivity-pipe.test.ts — real DuctSegmentNode
  cross-family isolation case (was waste-vs-vent), new pipe-trap anchor
  case (run drag doesn't move trap; trap drag still stretches run), and
  beforeEach/afterEach registry reset instead of leaky beforeAll

- nit: biome format/import-order on all touched files
This commit is contained in:
Open Pascal
2026-06-16 19:56:31 +00:00
parent 54a24e4c5c
commit 8eab9979ab
8 changed files with 121 additions and 97 deletions
+14
View File
@@ -720,6 +720,20 @@ export type NodeDefinition<S extends ZodObject<any>> = {
* Kinds outside any distribution system leave this unset.
*/
distributionRole?: DistributionRole
/**
* When `distributionRole` is `'fitting'`, controls whether this fitting
* is dragged as a rigid follower when a connected run endpoint moves.
*
* - `true` (default for `distributionRole === 'fitting'`): the fitting
* translates rigidly so its mated collar stays on the moved port — the
* right behaviour for in-line fittings (elbows, tees, wyes, crosses).
* - `false`: the fitting is anchored in space; moving a connected run
* endpoint stretches the run arm, not the fitting. Use this for
* fixed-position fixtures like `pipe-trap`.
*
* Has no effect when `distributionRole` is not `'fitting'`.
*/
portConnectivityFollow?: boolean
defaults: () => Omit<z.infer<S>, 'id' | 'type'>
migrate?: Record<number, (old: unknown) => unknown>