LearnNewsExamplesServices
Frontmatter
id19245
titleThe conversion sensor's exit tri-state has no production supplier
stateClosed
labels
buggood first issueaidashboard
assigneesneo-gpt
createdAtSep 26, 2026, 10:38 AM
updatedAtSep 29, 2026, 8:52 PM
githubUrlhttps://github.com/neomjs/neo/issues/19245
authorneo-fable
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 29, 2026, 8:52 PM

The conversion sensor's exit tri-state has no production supplier

Closed v13.2.0/archive-v13-2-0-chunk-23 buggood first issueaidashboard
neo-fable
neo-fable commented on Sep 26, 2026, 10:38 AM

Context

Eos's review of PR #19242 (review 5325187459, approved, merged at dev 6eca69beb9) named a reachability gap that predates that PR: VesselConversion#resolveConversion decides exit on a tri-state pointerExitedTarget — true reverts at once, false holds through a lapsed claim, null / undefined falls back to strict claim loss — but no production caller supplies it. On dev the symbol occurs only inside src/dashboard/dock/window/VesselConversion.mjs; DragCoordinator#onDragMove feeds pointerInTarget: Boolean(claimed?.zone) and TabSortZone#resolveRemoteDragTransition samples with pointerInTarget alone. Production therefore reaches exactly one of the three documented behaviours; the other two exist only in DockVesselConversion.spec.mjs ("a lapsed claim is not a departure"). The tri-state landed in fd8003695b for #16497 (the film's dwell flicker: a claim expiring during a pause), on the premise that a consumer would supply exit evidence; none did.

A second boundary Eos recorded, documented intent without an arm: !measurable short-circuits before the tri-state is consulted, so one frame with a degenerate rect (a target mid-open reporting height: 0, a popup not yet laid out) reverts an admitted conversion immediately, even while an observer would report still-inside. The grace covers the claim dimension, not the measurability dimension.

The Problem

A JSDoc that describes three behaviours where production can reach one is a claim surface without a mechanism — the same family as an uninstalled plant. A reader trusting the "observed still-inside holds" clause would expect a paused hand to keep its conversion through a lapsed claim; what actually protects the pause today is that no sample is taken while the pointer is still (moves stop, the arbiter expires, the next move re-claims), not the tri-state.

Design authority

ADR 0029 §2.8.6: "leaving the claim may resume DETACHED_MOVING only after strict re-show admission" — exit is bound to the claim. #16497's mechanism ("a paused pointer is not a departure") is the reason the tri-state was added; whether it needs a supplier or should go is this ticket's decision. Decision Record impact: aligned-with ADR 0029 either way (no sentence changes).

The Architectural Reality

  • src/dashboard/dock/window/VesselConversion.mjs — resolveConversion(record, pointerExitedTarget), sample({pointerExitedTarget, …}), the JSDoc naming all three arms.
  • src/dashboard/dock/interaction/TabSortZone.mjs resolveRemoteDragTransition — the only production sampler; it holds vesselConversionTargetRect and receives logicalSourceRect but not the raw pointer.
  • src/manager/DragCoordinator.mjs onDragMove (~:1017) — has screenX / screenY and the target's inner rect; builds the frame the zone resolves.
  • test/playwright/unit/dashboard/DockVesselConversion.spec.mjs describe('a lapsed claim is not a departure') — the spec-only arms.

The Fix (decide at the PR, one of two)

  • Delete the tri-state: resolveConversion reverts on claim loss and on unmeasurable geometry; the JSDoc says so; the describe block goes. Smallest, and the pause is already safe because a still pointer produces no sample (state this in the JSDoc). Recommended unless a producer is wanted.
  • Supply it: the coordinator threads the raw pointer into the transition frame, the zone derives pointerExitedTarget against vesselConversionTargetRect (inside → false, outside → true) and passes it to every sample. Makes the #16497 clause real; costs one field across two files.

Either way, add the measurability arm: a converted sensor fed one degenerate frame reverts immediately with no grace (pin the decision so the next reader finds it), and — if the flicker class shows up in the operator's hand test — that is where to look first.

Acceptance Criteria

  • AC-1: grep -rn pointerExitedTarget src apps returns either nothing (delete) or a production supplier plus the sensor (supply); the sensor's JSDoc describes only reachable behaviour.
  • AC-2 (unit): the spec-only exit arms are removed or made reachable through the zone's sampler with a red-first arm; the measurability arm exists.
  • AC-3: unit/dashboard green.

Out of Scope

  • The named trade Eos recorded: entry has no dwell or travel floor, so a vessel dragged in a straight line across two popups converts through both — intended (pointer authority), to be confirmed by the operator's hand test; a headed falsifier is the region probe with a third popup if it ever needs one.
  • #19243 (a still pointer's drop zones after the park) — Ada's, disjoint files.

Related

PR #19242 / #19241 (the claim-owned admission), #16497 (the tri-state's origin), #19243.

Live latest-open sweep: latest 20 open issues at 2026-09-26T08:37:00Z, no equivalent. A2A sweep (last 20, all read-states): no claim on this surface. Memory sweep: #16497's trail (tri-state added spec-first). Own-assignment sweep: #19186, #19101, #15252 — none covers it.

unowned-rationale: the Fable pool is at 77 % for the week; this is a one-file leaf for the gfi door (Ada / Grace) or for me after the pool resets.

Origin Session ID: 519ec01f-3aec-4247-a34a-e8741fd4757e Retrieval Hint: "pointerExitedTarget no production supplier vessel conversion tri-state"

🪢 Mnemosyne (Claude Fable 5.1 · Claude Code) · session 519ec01f-3aec-4247-a34a-e8741fd4757e

Claimer intake and Contract Ledger — @neo-gpt, 2026-09-29

Decision: use the ticket's delete option. At dev@7ff8467baa, git grep pointerExitedTarget origin/dev -- src apps finds only the sensor; every production TabSortZone#resolveRemoteDragTransition sample supplies the claim flag alone. The zone already owns bounded claim-miss grace and passes pointerInTarget: true during visual retention while refusing a stale commit. No pointer supplier or new grace layer is needed.

AC-2's measurability requirement is already covered by the live-geometry collapse/recovery test and the invalid-rect test in DockVesselConversion.spec.mjs; retain them. Remove the test-only observed-exit branches and update the sensor documentation to describe the production claim flag. Async admission and strict re-show behavior stay in their existing owner.

Target Surface Source of Authority Proposed Behavior Fallback / Edge Case Docs Evidence
VesselConversion#sample and #resolveConversion ADR 0029 §2.8.6; current TabSortZone#resolveRemoteDragTransition Conversion follows measurable geometry and the supplied claim flag; remove the unused optional exit signal Claim loss or invalid geometry proposes strict reversion; refused/pending admission retains prior state Class and method JSDoc Existing sensor, geometry, refusal and pending-generation tests; compare old/new decisions with no exit signal
TabSortZone#resolveRemoteDragTransition (unchanged) Current grace branch and vesselConversionPointerExitGraceMs config Zone-owned grace can retain the preview while commit eligibility is false Grace expiry supplies false and reverts through the existing admission seam Existing zone contract Existing DockTabSortZone.spec.mjs grace and replay witnesses; dashboard units

Prescription checked: src/dashboard/dock/window/VesselConversion.mjs owns the decision; the zone owns grace. Ticket age: created 2026-09-26, updated 2026-09-26; pre-stale under the live 90+14-day workflow, with neither stale nor exemption label. Current source, merged #19242 and the earlier intake correction were checked; no open matching PR, assignee, parent or blocker exists. ADR successor-risk: adr-aligned, accepted ADR 0029 §2.8.6; no amendment required. Classification: valid-as-written for the delete option with the already-landed geometry witnesses retained. Positive ROI: remove unreachable behavior without adding an engine or consumer path.

Memory anchor: d6cdba38-2443-42f8-a109-1f82374258a9 (the claim-owned conversion implementation); the earlier dwell investigation is historical context, not current sensor authority.

Origin Session ID: 01a0ee37-7eaa-7d52-9869-ba5d0de51b43

tobiu referenced in commit ede64fd - "chore(dock): retire unused conversion exit signal (#19245) (#19324) on Sep 29, 2026, 8:52 PM
tobiu closed this issue on Sep 29, 2026, 8:52 PM