Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .agents/inbox/2026-10-28-hidden-labels-need-valid-measurement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Keep conservative label geometry when hidden SVG measurement is empty

2026-10-28 UTC, independent review of item-wire source
`d9a0a5855612290b7c765999440b0d1d55b36333` for #220 / PR #221. Existing
guidance was searched in the symptom index and the offset-label lesson. This
was a source-review finding, not a reproduced deployed/browser incident.

The first placement pass can hide an edge-label group before its delayed
150ms sizing callback runs. Accepting an empty `getBBox()` then replaces a
conservative text estimate with padding alone (36 units), allowing a long
label to be admitted using an undersized obstacle/background. Initialize text,
icon and background from one conservative cache, and replace that cache only
when both measured dimensions are positive. Hidden labels must not become
small merely because they are not currently measurable.

The same review found a line-only diagnostic reading `x1.baseVal` after the
renderer changed to SVG paths. Read actual path endpoints through
`getPointAtLength` and transform them through `getScreenCTM`. Searching for
remaining `SVGLineElement` / `x1.baseVal` consumers found this one diagnostic.

A separate source audit found that directed perpendicular offsets put reverse
edges on the same lane: reversing both the direction and lane sign cancels the
separation. Use a canonical unordered endpoint direction to define lane sides,
while preserving the original route direction for arrows and editing.

Dated local commands used supported Node22.18, nice10, one worker, in the
existing clean-installed partial probe (not full-tree acceptance):

```sh
node node_modules/vitest/vitest.mjs run --root packages/web \
src/lib/__tests__/edgeRouteGeometry.test.ts --maxWorkers=1 --no-file-parallelism
node node_modules/typescript/bin/tsc --noEmit -p packages/web/tsconfig.json
node node_modules/@playwright/test/cli.js test tests/e2e/graph/item-wire-consumers.spec.ts --list
node --test --test-concurrency=1 tests/lib/runner/unified-cli.test.mjs
```

Results: 11 geometry cases passed in0.722s (9ms bodies); scoped web types passed
in3.72s; two browser cases discovered, **not executed locally**; 32 CLI harness
cases passed in4.56s without a browser. Scoped flat-config lint over both
components/helper/test passed with0 errors and314 warnings. An initial lint
invocation from the uninstalled worktree failed to resolve a legacy config;
the corrected command ran from the probe's web package with
`ESLINT_USE_FLAT_CONFIG=true`. That setup error is not a source lint result.

The new blocking `item-wires` sequence is enrolled in PR/full profiles and CI
after smoke, before longer suites, with one browser worker and zero retries.
It exercises actual SVG paint/hitboxes/arrows, pointer dragging, wire/label
editing and initially hidden label sizing, retains PNG/JSON, rejects writes,
compares original graph data and uses owned fixture cleanup. CI execution and
artifact review remain required. The route is still straight; these changes
do not establish bounded obstacle routing, visible detail budgets, complete
spaghetti detection or closure of #220.

Independent review of `9931b3c` required stronger browser oracles before
acceptance: pinned physics can be at rest while the450ms Fit transition is
still moving the camera, and paint checks must inspect ancestors and actual
stroke/fill. The correction waits for both `atRest` and `cameraReady`, rejects
hidden ancestors/absent paint, and deliberately hides wire and arrow groups or
removes a wire stroke to prove rejection. It also compares route endpoints to
the original API-directed cards and checks the editor's original direction and
selected type. Relationship type buttons expose their selected state through
`aria-pressed`. Local discovery still finds2 cases and scoped web types pass;
actual browser acceptance is pending. These were source-review findings,
not locally executed browser negatives.

On2026-10-28, exact source `93fe6d2` CI40646515194 failed the hidden-size case:
expected at least104, received94.90625. The other item-wire case passed,
including paint negatives and actual dragging/editing. Chromium had returned
valid measured text dimensions smaller than the conservative estimate; the
assertion incorrectly assumed hidden SVG always measures zero. This is a test
precondition defect, not evidence that valid94.90625-unit geometry clips text.
The amended case deliberately returns an empty box only for hidden edge text,
counts that branch and retains the full-size/background/visibility assertions.

The owner then requested a visible headed browser. A single installed Chrome,
one worker, zero retries, on isolated preview28917/API28918 and the previously
owned Neo4j28687 exercised the regression. With only the positive-measurement
production guard removed from the disposable preview, the actual case failed
with width36 versus104, after confirming an empty measurement. Restoring the
unchanged production guard made both item-wire cases pass in21.9s. Traces,
screenshots and logs are retained under `item-hidden-negative` and
`item-wires-headed-positive` in the20261027 evidence directory. This reused a
matching dependency installation for scoped preview evidence; it is not a
clean-install/full-tree acceptance run. The source PR remains subject to fresh
CI and independent review. The owner's further feedback about clipping,
overlap, tiny text and wasteful cards requires broader visual acceptance;
passing these two interaction cases does not resolve that feedback.
47 changes: 47 additions & 0 deletions .agents/inbox/2026-10-28-offset-label-drag-needs-offset-path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Project dragged labels onto their displayed offset path

2026-10-28 UTC, detailed item-wiring preparation for #220. Existing guidance
was searched through `.agents/issues/INDEX.md`, `edgeLabelLayout.ts` and the
schematic lessons. This is a controlled helper regression found before D3
integration, not an observed failure in a deployed item router.

The independent review of `eee4063c43e619a33c48fd0408bb9dc2dd45cc92` asked for
an offset-label drag case beside a close return wire. The helper initially
projected the pointer onto the wire centerline. On a U-shaped route whose
horizontal legs are 40 units apart, the lower leg's upright label is offset22
units upward. Its center is18 units from the upper wire and22 from its own.
Nearest-wire projection therefore selects the wrong leg even with the prior
fraction available as a tie-breaker: the distances are not tied.

Command, supported Node22.18, existing clean-installed partial probe, one worker:

```sh
node node_modules/vitest/vitest.mjs run --root packages/web \
src/lib/__tests__/edgeRouteGeometry.test.ts --maxWorkers=1 --no-file-parallelism
```

Before correction: 9 passed, 1 failed, exit1, 0.713s. The executed new assertion
expected `340 / 440 = 0.772727...` but received `100 / 440 = 0.227272...`.
It passed a requested offset22 while the earlier helper ignored that extra
argument. The production API was then extended explicitly, not the assertion
relaxed. After correction: 10 passed, no skips, exit0, 0.691s (8ms test bodies).
Scoped strict ES2020 TypeScript checking of helper and test also passed. Source
and test were copied unchanged into the probe; this is not full-tree acceptance.

Corrective rule: use the same upright perpendicular displacement for pointer
projection and label painting. At D3 drag start, retain where inside the label
the pointer grabbed it, then project the adjusted label center onto the offset
route. Preserve icon rotation around the icon's own center. Keep the direct
wire projection available for wire hit testing; it has different geometry.

The review also found repeated route scans and candidate sorting in placement.
Interpolate each candidate directly on its current segment and retain the best
candidate in one pass. Placement now has O(segments × obstacles + segments)
work with at most four candidates per segment. Runtime callers still need
explicit bounds and a suitable update cadence. A null sampled placement means
no tested slot was clear, not that every mathematical position is obstructed.

The regression lives in `edgeRouteGeometry.test.ts`. No browser/database/image
suite ran locally, no canonical positions changed, and no item-renderer
acceptance is claimed. Carry this finding with the D3 consumer fix and its
actual pointer/browser gates through normal review.
2 changes: 2 additions & 0 deletions .agents/issues/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

| Symptom | Canonical lesson and regression |
| --- | --- |
| Hidden item labels acquire tiny backgrounds, reverse wires share a lane, or line-only diagnostics fail after path conversion | [Preserve conservative dimensions and shared path consumers](../inbox/2026-10-28-hidden-labels-need-valid-measurement.md); item-wire browser sequence and opposite-direction lane regression |
| A dragged offset label jumps to a nearby return wire despite a prior-leg tie-breaker | [Project onto the displayed offset path](../inbox/2026-10-28-offset-label-drag-needs-offset-path.md); close-return-leg geometry negative and correction |
| Extra graph-fixture batches can continue while timeout cleanup deletes their graph | [Keep additional edges inside tracked seeding](../inbox/2026-10-28-fixture-extras-must-share-cancellation.md); delayed edge-response owned-fixture regression |
| A clean schematic metric passes while wires are invisible, or a new relationship button makes an old member locator ambiguous | [Require actual paint and distinct controls](../inbox/2026-10-28-schematic-gates-must-see-painted-wires.md); actual overview bounds/paint/direction checks and opacity-zero negative control |
| Open bus lists disagree with live groups, failed routing searches multiply work, or hidden-link counts omit a partition | [Resolve live memberships and cap the whole invocation](../inbox/2026-10-28-bus-views-need-live-membership-and-total-budgets.md); relationship/live-update regressions, budget negative, CI text-bound failure |
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ jobs:
env:
TEST_URL: http://localhost:3127
CI: true
- name: Item wire consumer acceptance
run: xvfb-run --auto-servernum node tests/run-unified.mjs --sequence item-wires --out test-artifacts/ci-item-wires
env:
TEST_URL: http://localhost:3127
CI: true
- name: New-user quality gate (headed under virtual display)
run: xvfb-run --auto-servernum node tests/run-unified.mjs --sequence new-user --out test-artifacts/ci-new-user
env:
Expand Down
68 changes: 68 additions & 0 deletions docs/architecture/schematic-connections.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,71 @@ spacing, readable names or a useful hierarchy.

This is a reviewable candidate path. It does not close dynamic LOD, hosted parity,
release/deployment work, or the remaining item-level visual problems.

## Detailed item wiring continuation (#220)

The first item-wiring increment starts from dev
`007148f1a526b8ef028222a429b346a67e5c3c2c`. `edgeRouteGeometry.ts` defines the
shared display path, final-segment arrow, distance along the route and pointer
projection needed by the D3 path/hitbox/arrow/label consumers. Consecutive
coincident points do not produce broken paths; a nonzero closed loop retains
its travel and direction. Label placement fits an entire label on one segment,
keeps text upright, retains original icon direction and returns no placement
when no sampled slot clears cards or previous labels. This is conservative
admission, not proof that every possible position is obstructed. Equal-distance pointer hits
prefer the prior leg so dragging does not jump across a nearby return wire.

The next committed increment connects this geometry to the D3 path, hitbox,
arrow and label consumers. It still supplies straight border-to-border paths;
this does not establish routed item acceptance. Next add bounded routing and
admission, named boundary connections, exact deferred counts and accessible
original relationships. Routing and obstacle searches belong to
bounded updates after layout or scope changes, not every camera/simulation tick.
Preserve edit/Grow, pointer hitboxes, label dragging and canonical positions.

On 2026-10-28, Node22.18 ran the nine pure geometry cases serially in the existing
clean-installed partial probe: 9 passed, no skips, 0.69s (8ms test bodies).
Cases cover elbows, reversed segments, closed loops, duplicate/invalid points,
nearest-leg dragging, ties, long labels and rotated obstacle footprints. This
is scoped helper evidence, not a full-tree browser/database/performance result.

Independent foundation review also required offset-aware dragging beside close
return wires. The executed negative selected fraction100/440 instead of340/440;
matching projection to the displayed offset corrected it. All ten cases passed
serially in0.691s and scoped strict ES2020 types passed. Candidate interpolation
now stays on its current segment and keeps only the best placement, avoiding
repeated route scans/sorting. The [canonical lesson](../../.agents/inbox/2026-10-28-offset-label-drag-needs-offset-path.md)
records the negative, correction and runtime integration limits.

The D3 adaptation caches label dimensions after initial text sizing, reuses the
path for paint and hit testing, places arrows from its final segment and slides
labels along its displayed offset. Culling composes with route/label visibility
instead of revealing deferred geometry. Label dragging updates only that label
against cached obstacles. The interaction diagnostic clicks the actual path
midpoint; the center of a bent path's bounding box need not lie on the path.

Scoped web typecheck initially rejected a D3 nullable-style overload; the callback
form corrected it, and the same check passed in3.66s. Scoped lint returned
0 errors and298 warnings in the existing large renderer; no warning-free claim.
These checks used the partial probe and are not actual browser acceptance.
All original runtime/visual/durability gates and new item-wiring cases remain
required before this branch can be integrated or #220 closed.

Review of the first consumer commit (`d9a0a58`) found empty measurement of
initially hidden labels and a line-only geometry diagnostic. The correction
initializes all label parts from conservative dimensions and accepts only
positive text measurements; diagnostics now read actual SVG path endpoints.
Opposite-direction parallel edges share a canonical lane normal so they stay
separate while their arrows retain direction. Wire and label clicks open the
same original relationship editor, whose controls now have accessible names.

Two real-browser cases are registered as blocking `item-wires` acceptance in
CI immediately after smoke (one worker, zero retries), retaining actual
paint/hitbox/arrow geometry and screenshots, pointer drag/edit behavior, hidden
label dimensions and unchanged graph data. Local discovery found both cases;
they have not run locally. The partial probe passed11 geometry cases in0.722s,
32 CLI harness cases in4.56s and scoped types; scoped lint reports0 errors,
314 warnings across the two components/helper/test. The
[review lesson](../../.agents/inbox/2026-10-28-hidden-labels-need-valid-measurement.md)
records the evidence and limits. These are straight item paths preparing the
bounded routing increment, not completion of #220.
Loading
Loading