LearnNewsExamplesServices
Frontmatter
id19584
titleFilm close: tear the matrix out, grow its window, throw the grid, bring it home
stateClosed
labels
enhancementaidashboard
assigneesneo-fable
createdAtOct 11, 2026, 3:09 AM
updatedAtOct 11, 2026, 5:00 AM
githubUrlhttps://github.com/neomjs/neo/issues/19584
authorneo-fable
commentsCount0
parentIssue15252
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtOct 11, 2026, 4:58 AM

Film close: tear the matrix out, grow its window, throw the grid, bring it home

Closed v13.2.0/archive-v13-2-0-chunk-25 enhancementaidashboard
neo-fable
neo-fable commented on Oct 11, 2026, 3:09 AM

Context

The operator, 2026-10-11 ~01:05Z, on the v1 draft of the re-take at dev 00b6f2aa6a (the first take whose close carries the sibling leaves' reveal and diagonal strokes): the drag-scroll inside the grid is far too slow, the grid's visible area in the room is tiny, he would have torn the matrix out and made the popup bigger (the film shows no window resize anywhere), and the drag-scroll should be fast and use the grid's own "throw" — on a fast release the grid keeps scrolling.

Design authority: the operator's design input (a peer design input, derived here: the hand-tempo rule sized the strokes like a walk, but a pan is a flick; the matrix at ≈ 300 px of width in the room can show neither its rows nor its canvas columns at scale; GridDragScroll's momentum is the grid's own feature and the film never shows it); the 2026-09-23 pacing rule (a real gesture at hand tempo, never a held frame — a flick is a hand gesture; momentum is the content moving, not a held frame); the approved A6 close (M08 the midpoint and the rest of the room breathing, M09 "The content never stopped living", E09 "And the work keeps going"), which a side-by-side ending fits unchanged — no spoken return is bound to the picture.

The Problem

The current film-signature scene (after the sibling leaves) brings the matrix and the feed in front, jumps the matrix to its midpoint, and pans it with three strokes of (−260, −120) at 36 samples × 33 ms — 1.2 s per stroke, ≈ 5 s in all — inside a pane ≈ 300 px wide. On film (take 2026-10-11T00-55-16-516Z, the #15252 re-take comment) the close reads as a slow crawl in a small box: the rows are legible only in the pane's strip, the sparkline column enters at ≈ 93 s after three strokes, and nothing demonstrates the two things a desktop user would do with that grid — tear it out into a window of its own and flick it.

The Architectural Reality

  • Tear-out and native return exist as film cues: apps/workstation/tour/fiveBeatFilm.mjs film-second-window uses {type: 'tear-out', itemId, sourceNodeId, options: filmPace} (NativeGestureDriver#executeTearOutStep, the vessel born under the pointer beyond the window's bottom-right, the pane's footprint); film-reintegration uses {type: 'native-return', ownerItemId, previewNodeId, options: {attempts, moveDelay, moveSteps}} (NativeGestureDriver#executeNativeReturnStep, which resolves the vessel's owner-granted route via Neo.manager.Window#resolveNativeRoute({capability: 'position', ownerWindowId, windowId}), reads the frame with Neo.Main.windowNativeGetGeometry, and moves it with Neo.Main.windowNativeMoveTo({...route, windowId: route.ownerWindowId, x, y}), a verified outcome per step).
  • Vessels are granted {close: true, position: true, resize: true} (src/dashboard/dock/Workspace.mjs:1127); Neo.Main#windowNativeResizeTo({nativeHandleKey, targetWindowId, windowId, width, height}) (src/Main.mjs:1244) resizes the OUTER frame through the same owner grant and verifies outerWidth/outerHeight reached the request (#resizeWindow, src/Main.mjs:974); the dock's NativeVesselTransaction uses the move for the park re-show, nothing uses the resize yet. The display's available area is readable through Neo.Main.getWindowData (screen.availLeft/Top/Width/Height, src/Main.mjs:343).
  • GridDragScroll (src/main/addon/GridDragScroll.mjs): the content follows the hand 1:1 during the drag; on release the velocity over the last 100 ms (px per 16 ms frame) decays with friction 0.95 until below 0.1 px/frame — a tail of ≈ 20 × v px, ≈ 2 s for a flick. GestureDriver#executeGridDragScrollStep already waits for that tail to settle and judges the travel against min(requested, room) (the #19580 Contract Ledger), so a throw's observed travel exceeds the request on the free axis and clamps at the room on the bounded one.
  • The witness test/playwright/e2e/workstation/WorkstationFilmTourNL.spec.mjs asserts ≥ 4 popups born and all closed at the end; the signature's final topology-assert pins split-main = ['scale-tabs', 'heavy-tabs'] with scale active — both hold when the matrix window comes home before the readout.

The Fix

Rewrite the film-signature steps after the two reveal ops (scale-tabs → scale, bottom-tabs → feed):

  1. tear-out of scale from scale-tabs (existing cue): the matrix leaves the room, a window born under the hand, the same instance riding along.
  2. New cue vessel-resize {type: 'vessel-resize', itemId: 'scale', frame: {anchor: 'right-of-main', gap, width, height}, options: {moveDelay, moveSteps}} → NativeGestureDriver#executeVesselResizeStep: resolves the vessel's owner route (capability resize, and position for the move), reads the frame, derives the target OUTER rect from the main window's frame (top-aligned, gap px right of its right edge; height defaults to the main frame's height; the display's available area from getWindowData clamps the target so a smaller display gets the biggest frame that fits, recorded in the proof), then walks position and size together from the birth frame to the target in moveSteps samples at moveDelay (windowNativeMoveTo + windowNativeResizeTo per sample, each a verified outcome; the film cursor is not involved — this is the window, not the hand). Receipt below. Listed in both cue vocabularies; TourController#executeCue routes it.
  3. scroll to index 50000 (existing cue) — the midpoint jump, now in a window with room.
  4. grid-drag-scroll as one throw: {paneId: 'scale', anchor: {x: 0.8, y: 0.85}, deltaX: -360, deltaY: -480, strokes: 1, options: {attempts: 300, curve: 'throw', moveDelay: 33, moveSteps: 24, showCursor: true}} — the samples share the travel eased in (quartic) at the film's 33 ms rhythm, so the stroke winds up and whips and the addon's momentum reads the last hundred milliseconds' speed; then the rows run on for ≈ 2 s; the x axis clamps at the matrix's right room (the sparkline and action columns in view), the y axis runs well past the hand. The pane cache entry's windowId follows the pane into the vessel, so the existing executor dispatches into the popup window.
  5. native-return of scale home — measured out (2026-10-11 02:xxZ, headless at the branch head): a vessel holding one item torn out alone carries a tab-keyed detached record (dockGroupNodeId: null), so the dock's native return adopts nothing from it (the frame moved through the targets, no transfer row appeared); the stack-return of its grip never settles (an active gesture, the main refresh pending, 200 s); a window close is an undoable close, not a return (windowNativeClose succeeded, the home held nothing); an undo of the tear-out brings the matrix home but leaves the emptied window open (the #19583 class). The film ends side by side instead: the room breathing and the matrix streaming in its own window, which the approved A6 close fits unchanged. A road home for a lone vessel is the product's open gap (two defect-notes filed; Ada's headless witness hit the same stack-return stall under #19583); no manual grip return has been verified either way, so none is claimed.
  6. The final topology-assert pins the stored home's survival (scale-tabs stays a tabs node — Security holds it) and the feed in front; the living close stays.

Captions name what each beat shows; the scene's beats list follows. targetSeconds stays a budget (10 s; the picture ≈ 12–14 s).

Contract Ledger

Cue {type: 'vessel-resize', itemId, frame, options?} — consumed by TourController#executeCue → NativeGestureDriver#executeVesselResizeStep({itemId, frame}, options); listed in WORKSTATION_CUE_TYPES and filmCueTypes.

  • itemId (String, required): an item currently committed in its own vessel (the tear-out's receipt), else applied: false by name.
  • frame (Object, required): {anchor: 'right-of-main', gap: Number px (default 24), width: Number px (required), height: Number px (default: the main window's outer height)} — the target OUTER frame, derived at run time from the main window's frame and contained within the display's available area (getWindowData) on all four edges — past the right edge it shifts left over the main window, past the left edge it shifts right, above the top it comes down, past the bottom it keeps its top and shrinks, wider than the area it shrinks to it; the origin may be negative and the main frame partly outside; the clamp recorded, an unknown display clamps nothing.
  • options.moveDelay (ms between samples, default 33), options.moveSteps (samples, default 16).

Receipt {applied, errors, proof}; proof = {before: {x, y, width, height}, target: {x, y, width, height, clamped: Boolean}, after: {x, y, width, height}, samples: Number, route: {windowId}}.

  • applied: true only when every sample's move and resize were verified by the main thread and the final frame is within 2 px of the target on every side.
  • Failures by name: no committed vessel for the item; no owner-granted route; a refused sample — the receipt's after is the frame as it stands, read back after the refusal (before stays the birth frame); when that readback itself fails, after is null and errors names the blind observation; a final frame off the target.

The throw reuses grid-drag-scroll's ledger (#19580) plus one option: options.curve ('linear' default | 'throw' = the samples share the travel eased in, quartic); per axis expected = min(requested, room), observed ≥ expected − slack; the witness pins the momentum (observed.y ≥ 2 × requested.y) because that is what the beat claims.

Decision Record impact: aligned-with ADR 0029 (native window operations through the owner grant; no placement-rule change — the grow is a scripted frame operation like the park re-show, not a drag).

Acceptance Criteria

  • vessel-resize is handled by TourController#executeCue, listed in both cue vocabularies, and the screenplay validates (fiveBeatFilm.spec); unit controls on NativeGestureDriver.spec: the frame walks to the derived target (move + resize per sample, verified), a display clamp is recorded and honoured, a refused sample fails by name with the frame as it stands, a missing vessel fails by name.
  • The headless film witness: the vessel-resize receipt applied with after within 2 px of target and after.width × after.height ≥ 4 × the frame it was born with; the throw's grid-drag-scroll receipt applied, strokes: 1, after.settled, travel.observed.y ≥ 2 × travel.requested.y (momentum), travel.met both axes; ≥ 5 popups born, every one but the matrix's closed; the final readout pins the stored home's survival and the feed in front. (Reconciled with the measured shape: no return home — see The Fix, 5.)
  • The scene's picture at the headless clock: tear-out + grow + jump + throw ≈ 11–12 s; no sample slower than hand tempo, no held frame (the pause floors stay concurrent with their cues).
  • Post-merge, deferred to #15252: the take's frames show the matrix window growing beside the room, the rows streaming after the flick with the sparkline column in frame, and the living close holding the room and the window together.

AC-1–AC-3 evidence: PR #19587 (AC Evidence table; GestureDriver.spec 67/67, fiveBeatFilm.spec 7/7, WorkstationFilmTourNL 3/3 headless, the close ≈ 11.5 s at the headless clock), approved by Emmy (review 5481660094 at 541781bebd), merged as dd870ec227 on 2026-10-11. AC-4 stays with #15252: the take at a head that also carries the fix for #19588 (the popup's Canvas-worker connection before the offscreen transfer).

Out of Scope

  • Any engine change to the dock, the vessel birth size (the pane's footprint stays the product contract), the drag-scroll addon or its friction.
  • The vessels of the journey scene and their choreography.
  • Narration: A6 stays whole; the edit layer re-binds the close only.

Avoided Traps

  • Growing the vessel at birth (a product change to the dock's footprint contract) instead of a scripted window operation after it.
  • Faking the throw by a scroll jump: the beat's claim is the grid's own momentum, measured by the receipt.
  • Bringing the matrix home by any cue that only looks right: the native return, the stack return, a close and an undo were each measured at the branch head and each failed by name (The Fix, 5); the witness's invariant became "every window but the matrix's closed" and the readout pins the stored home's survival, rather than asserting a return the dock does not perform.
  • Hard-coding the stage display's coordinates into the screenplay: the target frame derives from the main window and the display at run time.

Related

#15252 (the film epic; the take and the edit) · #19579 / #19580 (the sibling close leaves, merged as #19581 / #19582 at 00b6f2aa6a) · #19571 (take holds) · #19568 (hand tempo) · ADR 0029

Live latest-open sweep: the latest 20 open issues at 01:07Z, no equivalent (#19569 / #19583 concern re-born windows, not the close). A2A claim sweep (last 30, all read-states): no claim on the close. MC sweep ("film close drag-scroll slow, matrix tiny, tear out, bigger popup, throw"): only this lane's own turns. Own-assignment sweep: #15252 (parent), #19382 — none overlapping.

Origin Session ID: 9f6cd919-c61d-434d-a709-c700109af56b

Retrieval Hint: "film signature vessel-resize tear-out matrix throw grid momentum native return home"

tobiu referenced in commit dd870ec - "feat(workstation): the film's close tears the matrix out, grows its window and throws the grid (#19584) (#19587) on Oct 11, 2026, 4:58 AM
tobiu closed this issue on Oct 11, 2026, 4:58 AM