Repository navigation
Release Type: A patch release: the fixes from one day of ordinary-use testing of the Workstation, the night that followed and the day of the cut, two days after 13.3.0. The package's entry points and dependencies are unchanged; the small additions are opt-in or additive.
Stability: Stable. Set at the candidate dev 36a74c7 after the release gate (#20022): the hosted e2e tier green on the candidate, three serial full runs of the Workstation and dashboard e2e directories with their few reds each named as the harness and passing alone, and the operator's own manual walk of the Workstation, which found two defects: one fixed on the candidate (#20082), one minor, left open and named under Honest bounds.
Upgrade Path: A drop-in replacement for 13.3.0; see Upgrading from 13.3.0 below.
TL;DR: The morning after 13.3.0 shipped, a few minutes of ordinary use of the Workstation, maximizing panes, dragging tabs, popping panes out and back, reloading, found eleven defects that forty-two Workstation test files had not. By evening all eleven were fixed on
dev, each with a test that is red on the code before it, and the question of why the tests had not seen them became a design discussion; the full test runs and a second look over the night and the next morning found eight more at the same edges, fixed the same way. On the day of the cut, with every fix in and the suites green, the operator ruled that no known Workstation, dock or grid bug ships: the ruling put three findings of a chartered walk and three standing notes from the week's test batteries on the blocker list, and his own walk of the app found four more in under an hour, at gestures no test performed, all four fixed and merged the same day, three of them within two hours of their report, each with the gesture as a test; his final walk on the gated candidate found two more, one fixed the same evening and one left open and named. 13.3.1 ships those fixes, the grid header-menu follow-ups from the first outside feedback on 13.3.0, and five VDOM lifecycle repairs. No migration is required: the package's entry points are unchanged, the fixes and the behaviour corrections listed under Upgrading apply on update, the one new setting, hover reveal of header actions, is opt-in, and the one new method,Store#asContinuation, is additive.
📋 13.3.1 in 2 Minutes
New to Neo.mjs? It is an agent institution, persistent AI maintainers with identities, memory and peer review, and the multi-threaded application engine they build, in sibling open-source repositories (README). This note covers the engine. Your app's components, state and data stores run in a worker, the App Worker; the main thread applies DOM changes and answers layout questions.
Who built it: this release's pull requests came from Ada (@neo-opus-ada), Clio (@neo-fable-clio), Grace (@neo-opus-grace) and Vega (@neo-opus-vega) on Claude, Julia (@neo-gemini-julia) and Maryam (@neo-gemini-maryam) on Gemini, and three outside contributors, @bercedev, @Jah-yee and @arnavsingh0418. The maintainers review each other across model families, and Tobi (@tobiu) merged every pull request in this release except five automatic Dependabot bumps.
The one line: the Workstation's edges hold.
What changed:
- Workstation and docking (#19982–#19993, #20004, #20006, #20012, #20017, #20018, #20025, #20029, #20041, and the cut day's #20045, #20046, #20047, #20057, #20070, #20072, #20073, #20075): maximized panes, tab drags, popups and tear-outs, layout persistence, undo across windows and the 100k-row grid behave at their edges.
- Grid header menus (#19952–#19955, #19961, #19965, #19977): seven follow-ups to 13.3's new feature from its first day in the open.
- Grid (#20056, #20058, #20082): a live feed keeps the reader's scroll, a value menu's Clear keeps the keyboard inside the menu, and a grid shown again or moved in the DOM keeps its scroll position.
- VDOM lifecycle (#19963, #19966, #19969, #19973, #20043): an update that lands after an unmount, a hole in a child array, a hidden floating child and a component that renders itself away no longer leave the tree inconsistent.
The scale: 65 pull requests merged in neomjs/neo and 63 issues closed since 13.3.0 shipped on 2026-10-21. The Full Changelog lists them.
Read for your work: Five minutes of ordinary use and its The day of the cut if you build docking layouts, Header menus after the first feedback if you use columnMenu, Upgrading from 13.3.0 if you ship the engine.
🧪 Five minutes of ordinary use
13.3.0 was released on 2026-10-21 at 10:40Z. The next morning, a short manual session with the Workstation, the dense docking app the engine ships as its reference, produced seven bug reports in a few minutes, then four more on a second look: a maximized pane whose tab overflow kept its pre-maximize partition, an overflow menu that closed as it opened, a tab header that flew to the window's top-left corner during an in-window drag, a duplicate header after moving a tab into a popup, a reload that silently dropped the saved layout, and a 100,000-row grid that pushed the layout past the viewport and then took the browser tab down with it. Eleven defects and one enhancement request were filed within the hour (#19982 through #19993), with a pointed question attached: the Workstation had forty-two end-to-end test files, so how had none of this been seen?
By the evening of the same day all eleven fixes were merged, each with a Neural Link e2e test that fails on the code before it. Eight more, found while fixing the first batch, in the full test runs and on a second look, followed over the night and the next morning (#20004, #20006, #20012, #20017, #20018, #20025, #20029, #20041). What they fixed, by surface:
Maximized panes
- The tab overflow partitioned the strip for the wrong width (#19982 → #19996). Maximize animates the pane with a transform, and the overflow plugin measured the strip's visual box inside that animation, so a maximized pane kept the partition of its small self: two of twelve tabs visible, with room for eight. The plugin now measures the layout box and partitions for the settled size, the discipline the grid's containers adopted earlier (#16375).
- The overflow menu closed as it opened (#19984 → #19997). A maximized pane is positioned relative to the viewport, so it escapes the
overflow: hiddenof its original split container.DomAccess#getClippedRectstill clipped the menu against that container, found an empty rectangle, and hid the menu four milliseconds after it mounted. The walk now honors CSS containing blocks. - A tab drag in a maximized pane previewed drops into the layout behind it (#19986 → #20000). Cross-zone drops are suppressed while a pane is maximized; in-strip reordering stays.
- A tab click inside a maximized pane flashed it back to docked size (#20029 → #20031). The staged shell swap re-derived a retained strip's classes and dropped
neo-dock-maximized, so for about 50 ms the pane laid out at its docked width and then showed one of its eight headers until the plugin re-applied the presentation. The maximize plugin now names the maximized node and the layout adapter projects the class onto it, so the swap keeps it. Found by tracing a hosted test failure rather than retrying it; see What the tests learned.
Tab drags
- A header flew to the window's top-left (#19988 → #20005). The sort zone's drag snapshot included tab items the overflow was withholding from the DOM, so the snapshot positioned an element that had no box. Unrendered items are excluded from the snapshot.
- A reorder painted the headers the overflow withholds (#19987 → #20008), a flash of three to five frames on every reorder in a dense strip.
- The Workstation's grid column drag proxy lost its skin (#19985 → #19999): the proxy is mounted on the document body, outside the app's themed root, and the app's SCSS now reaches it there.
Popups and tear-outs
- A tab moved into a popup left its header behind (#19989 → #20003). The source strip's header had been detached before the transfer committed and was never retired. This one is a useful falsifier for the tests, below: the items, the VDOM and the DOM all agreed, while the strip on screen disagreed with its committed document.
- Drags inside a popup followed the main window's rules only halfway (#19990 → #20010): a popup's tab strips now carry the main window's full drag contract, dock zones and tear-out included.
- A drag inside a popup lying over the main window was carried into main (#20004 → #20016): a point inside the source's frame belongs to the source for the whole gesture.
- The tab a popup was opened for could not tear out of it (#20006 → #20019). A vessel was identified by the item it was opened for, so that item's own tear-out resolved to the popup it was already in. A vessel is now identified by a Group key minted at admission; the tab leaves into a vessel of its own and the popup keeps its other tabs.
- Undoing a popup's native close reopened it with a duplicate tab header (#20025 → #20030). While the popup was closed, its projection of the pane's return waited for a remount, so its strip kept the returned pane's button over an empty body, and Undo paired the returning pane with a fresh button beside the old one. The reconciler's pre-pass now retires a button whose item is desired again when the resolver places that pane outside the body.
- A tear-out released late reported an error over a commit that had landed (#20018 → #20037). When the release came after the vessel window had loaded, the owner change and the commit both bound that window and mounted it twice; the second mount found no provisional chrome left to retire and threw. A second binding now joins the mount already in flight.
- A root-edge drop wrapped the layout but left the rails inside (#19992 → #20001): the rails are window chrome and frame the whole arrangement.
- A malformed placement fallback was repaired into a valid one (#20012 → #20013) instead of being refused by validation.
Persistence
- A reload silently dropped the layout (#19993 → #19995). After popups had been opened and closed, a placement hint still named a removed node; capturing the layout for auto-save refused it, and the refusal was not logged. The hint no longer stops the save.
Departed windows
Three defects share one shape: a window leaves (a popup closes, a page reloads) while work addressed to it is still in flight, and the work lands somewhere it should not.
- A reload that landed while a dock projection was running left the remounted panes hidden (#20017 → #20032). The projection kept running into the window that had left, the layout's adoption into the reloaded window moved the projection's host under it, and the projection never settled, so its staged shell stayed hidden over the panes it had taken. The dock now parks a projection for a departed window and lands it on the next mount. The serial test runs reproduce the trigger, a reload mid-gesture, in 6 of 11 attempts; a person at the keyboard cannot reload in the middle of a drag, so for hand use the trigger is theoretical, and the fix is insurance that a reload under any load leaves no pane hidden.
- A pane restored to the main window after a popup cancel vanished from it (#20041 → #20042). A VDOM reply computed for the departed popup was relayed to the main window, the relay that keeps a flight from wedging, and the main thread applied its deltas as its own, removing the pane it had rendered three milliseconds earlier. The main thread now applies only its own window's DOM updates and answers a foreign batch the way a departed port does, so the flight settles silently. Measured at 2 of 86 attempts under CPU throttling alone and far more often under full-run load.
- A message for a departed window without a routing key could reach another window (#20026 → #20027). The worker's last-resort routing now skips a known-departed destination. Measured across the suites, every such message had been the removal of a drag proxy for a window that had left, harmless where it landed; the hole is closed before it finds a harmful case.
The 100k Matrix
- A grid without a size constraint wedged the App worker (#19991 → #19998). When the Matrix pane's container became content-sized, the grid measured its body's full virtual height, 5,000,050 pixels for 100,001 rows, built a pool of 99,970 row components, and threw a
RangeErrorspreading them into its items after every row had already registered its id. The App worker stopped answering, Chrome climbed to about 160 % CPU, and the tab crashed. The grid now keeps a window-sized viewport and row pool whatever its ancestors do. What made the container content-sized in the first place is still open (#20009); see Honest bounds.
The enhancement
- Header actions on hover (#19983 → #20011). A dock pane's focus-gated header actions can now also reveal on pointer hover:
showOnHover: trueon a toolbar, orheaderActionsShowOnHover: trueon a dock Workspace. The overflow reserves the revealed cluster's width up front, so hovering a dense strip never re-partitions it. Off by default.
The day of the cut
The cut was scheduled for the morning of 2026-10-23, with every fix above merged. The operator's ruling that morning set the bar for a patch whose claim is stability: every known Workstation, dock or grid bug blocks the release, whatever its size, and the cut is the head on which his own walk of the app finds nothing. Eleven more defects met that bar during the day; all eleven are fixed here.
Three came from the chartered walk that followed the ruling:
- A popup closed without
beforeunloadnever returned its pane (#20045 → #20053). The engine announced a window's departure only inbeforeunload, which an automation's close (Playwright'spage.close(), CDP's target close) does not fire, so the SharedWorker kept the dead window's port and its vessel binding: the pane never came back, Undo reverted the row before, and the vessel's components kept updating a window no longer there until the watchdog called them wedged. A window now departs at itspagehideunless the page enters the back-forward cache. - The overflow control of an unfocused pane needed two presses (#20046 → #20052, by Clio). The first press focused the pane, which revealed the focus-gated action rail beside the control and re-partitioned the strip under the pointer, so the release landed on another button and no click fired. The toolbar can now hold a focus-gated reveal back for a named reason; the overflow plugin holds it for exactly as long as a press can still click the control and hands it to the menu at its mount. No timer decides when a press ends.
- Undo after a popup's native close emptied the other popup's window (#20047 → #20049). The replay embodied or closed a popup's window only for the native-close return row, so undoing a pop-out sent the pane home and left its window open with nothing in it. Every undo and redo a popup receives now follows one rule on its own post-cursor document: a replay that empties a bound popup closes its window, one that fills a popup without a window embodies it first.
Three were standing notes from the week's test batteries, blockers once the ruling named them:
- The Workstation's live feed grid snapped back to the top on every tick (#20056 → #20059). Each tick's add and trim ran as ordinary Store loads, and the grid resets its scroll on every load that is not a continuation, so a reader could not stay anywhere below the newest rows.
Store#asContinuation(fn)runs a callback's synchronous mutations as a continuation; the Feed runs each batch inside it. - The drag-end focus restore fired before the dragged item was visible again (#20057 → #20063). The sort zone re-showed the dragged item through an unawaited update and focused it after a fixed 30 ms; under load the focus landed on a still-hidden element, so the strip stayed unfocused and its header actions hidden, the mechanism of one hosted intermittent. The restore now awaits the owner's update promise first.
- A grid value menu's Clear dropped focus to the page (#20058 → #20060), so Escape no longer closed the menu. Clear hands focus to the menu's search field before it hides itself.
Four were the operator's own findings on the head with every fix in, each read live through the Neural Link before a line of source was opened:
- A tab dropped on an edge of its own pane previewed a split and landed nothing (#20070 → #20071; reported 16:48Z, merged 17:18Z). The release re-derived its preview with the drag's own node filtered out while the hover included it, so the hover promised a split the release never performed. The release now reads the zones the hover read.
- Tab siblings kept reordering while the drag was far outside their strip (#20072 → #20074). The sort zone reorders on its axis alone, so a dock drag 300 px below the strip still slid the header it had left. A dock strip now parks its reorder while the pointer is beyond the release band the drop already voids on, and resumes from the live pointer when it returns; plain tab strips, maximized dock strips and the grid header keep following a loose hand, which is wanted there.
- A tab dragged from its popup into main left its header proxy behind (#20073 → #20076). The drop's own popup projection destroyed the dragged tab button before the cross-window commit settled, so the proxy's identity read as undefined and nothing retired the proxy. Participation keeps the identity each frame staged under, and the proxy embodiment reports a terminal that misses a live generation instead of leaving one on screen.
- Closing a popup collapsed main's dock layout to its content size (#20075 → #20079). After one of the popup's tabs had been dragged into main, the close rebuilt main's shell from containers carrying
flex: noneand nulled sizes their configs never set. The writer: the drop's own projection destroyed the popup's tab header toolbar and button while the sort zone's drag end was still waiting out its latch, and its owner and item restores then ran on the destroyed components;Base#destroydeletes an instance's own keys, so the style write landed in the class's shared VDOM template, and every container created afterwards inherited it. The drag end now restores nothing onto a destroyed component, andComponent#destroyleaves the instance a private copy of its template, which also contains the other after-destroy writers a census of the unit suite found.
His final walk on the candidate, with the hosted tier and the serial runs green, found one more that blocked:
- The 100k Matrix grid in a popup rendered no cells after a dock gesture (#20082 → #20083). Read live before anything was touched: the grid body's and the header's engine
scrollLeftwere 762, exactly that 318-px viewport's far-right limit, while the DOM stood at 0, so the column window rendered cells 380 to 1080 px off-screen and the in-viewport cells carrieddisplay: none. The measured trigger: after the grid had been scrolled down and to the far right, adding a second tab to the Matrix's stack and clicking the Matrix tab again (its card hidden, then shown) reset the DOM's offsets to the origin, as a browser does when a scroll container stops being laid out or moves, with no scroll event for the engine to echo; a split beside the stack did the same. The grid container's resize handler now ends by reading both offsets, and an axis the DOM holds at the origin while the engine does not gets the engine's position back through the same path a programmatic scroll uses; a DOM away from the origin is a live scroll and stays untouched.
Honest bounds
- The eleven batch fixes carry a Neural Link e2e test under
test/playwright/e2e/workstation/ortest/playwright/e2e/dashboard/that is red on the code before them. Of the follow-ups, #20016, #20019, #20030, #20031, #20032, #20037 and #20042 each add e2e and unit arms; #20013 and #20027 are covered by unit contracts alone, the latter with its effect measured by a drop census over the full suites outside CI. The hosted Brain-gated tier (#19705) runs the full set on everydevpush; on a pull request it runs the full set when the change reaches the e2e impact boundary and a smoke subset otherwise, with its exclusions declared in the workflow, so no single pull request's run exercises every fix listed here. The cut day's eleven fixes carry the same shape: a Neural Link e2e on the Workstation or onexamples/grid/issuesfor every one of them (#20049, #20052, #20053, #20059, #20060, #20063, #20071, #20074, #20076, #20079, #20083), unit arms beside all but #20060, and two of the four dock gestures (#20071, #20074) reproduced under a 4× CPU throttle because the operator's hand found them on a loaded machine. - The 100k Matrix crash is fixed at its engine cause and the symptom is bounded. The trigger, what made the pane's container content-sized during three dock operations and a grid drag, has not been reproduced headless (#20009). A measurement on the fixed code found one OffscreenCanvas per pooled row node: 32 nodes for the 100,000-record store, the same 32 row and canvas nodes moving across the records as the grid scrolls with no new transfer, a pool that grew to 39 nodes under a forced content-sized chain and returned to 32, none failing, and the worker answering in one to two milliseconds throughout. The cells reuse their nodes; what amplified the crash before #19998 was the 99,970-node pool itself, which #19998 bounds.
- Running the full Workstation and dashboard e2e directories in one session surfaced four failures that the same files rarely show alone. All four are explained rather than retried: two were product defects, fixed here (the hidden panes after a reload, #20017; the pane that vanished from the main window after a popup cancel, #20041), and two were test oracles reading a tab strip before its partition had landed, repaired in the specs (#20020 → #20021, #20033 → #20039). The engine hole underneath #20017, an update promise on a mounted component without a vnode that no flight settles, is closed here as well (#20043 → #20044); the report it adds found, on its first red run, the grid search toolbar's filter summary and Clear button hiding themselves that way, a state 13.3.0 left to converge on a later parent update. The gate's later runs and the hosted tier added three test defects, repaired in their specs (#20050 → #20051, #20054 → #20055, #20068 → #20069), and one product defect only load shows, the drag-end focus restore under The day of the cut.
- Of the two defects the operator's final walk found, one ships open, by his word minor and not release-blocking: in a narrow popup window a tab header title capped to an ellipsis keeps the ellipsis after the strip has room again (#20081); the overflow plugin clears its cap only on its next projection, which that room change did not trigger. It is the one known defect in this version at the cut.
🔽 Header menus after the first feedback
13.3's header menus met their first outside users on release day. Seven follow-ups ship here:
- A right-click on the header's menu arrow showed the browser's context menu on top of the grid's (#19952 → #19959, by @Jah-yee).
- The first filter pushed the grid and an open menu down by the summary row's height; the summary now shares the search controls' row (#19953 → #19956).
- Focusing a header button scrolled the header away from its body; a header that scrolls itself into view brings its body along (#19955 → #19957).
- A right-click on a header whose menu is disabled, in a grid that has menus, shows a short hint instead of nothing (#19954 → #19960).
- A value picked before the menu's search field took focus lost its focus to that field (#19961 → #19962).
- Enabling
columnMenuat runtime threw a VDOM root-mismatch error (#19965 → #19968). - Dropping a dragged column reset the grid's horizontal scroll (#19977 → #19978).
🧰 Also in 13.3.1
VDOM lifecycle. A missing child in a cn array, a hole, null or undefined, renders nothing instead of failing the whole flight and wedging later updates (#19963 → #19964); the index loop that replaced forEach in February had stopped skipping holes. An update that lands after a component's unmount(), or after an ancestor's, leaves the children unmounted instead of marking them mounted (#19966 → #19967, #19969 → #19970). A hidden floating child is unmounted together with the DOM its parent removes (#19973 → #19974); this was the cause of an intermittent menu test. A component whose own update renders its root away, hidden set together with another config, unmounts instead of staying mounted without a vnode, a promise asked while it is away settles when its parent shows it again, and any other way into that state is reported on console.error with the component's name (#20043 → #20044).
Containers and utilities. A container keeps an item whose construction adds a sibling during createItems (#19933 → #19946, by @bercedev). Rectangle#intersects returns a Boolean for a zero-size rectangle, and Rectangle gains containsPoint() (#20002 → #20007, by @arnavsingh0418, a first-time contributor).
Grid. A row-height reset retires the hidden ahead-measure projection it invalidates (#20028 → #20040), so measure nodes from the previous layout no longer survive a reset; this was the cause of one intermittent hosted test failure.
Contributor tooling and CI. The issue claim responder recognizes participle-led and #N claims (#19971 → #19972) and keeps queued claims from a burst while refreshing the issue's state (#19975 → #19976). The CodeQL extraction guard waits out the Actions jobs view, which can report a finished Analyze job as still running for up to half a minute, instead of failing the check on it (#20035 → #20038). Merged pull requests no longer read red from the mergeability controller's post-merge runs, which had rolled 19 of the 30 merges before the cut red (#20061 → #20062), and a green rerun of PR Baseline or File Size Guard repairs the same head's earlier red runs, which a body edit used to leave standing (#20064 → #20066). The hosted e2e tier's ten shards are split by measured time instead of file count, predicted at about 144 seconds each where the count split ran between 48 and 256 (#20077 → #20078); its first run, on this release's candidate, was green.
🔭 What the tests learned
The question that came with the bug reports got its own venue the same morning: Discussion #19994, Workstation E2E: discover interaction failures before release, opened by Sophie at the operator's request. Its finding so far is not that the tests were wrong but that they were narrow in a specific way: forty-two files, some of them already exercising transients, reloads and multi-step journeys, yet the eleven defects lived at intersections none of them crossed, maximize with overflow, popup with drag, history with reload, and in the frames between one state and the next. The fixture that fails a test on worker errors never saw a warning, and the layout's persistence failure came back as a receipt (persisted: false with its errors) that the commit listener discarded, so nothing reported it.
Two things changed for this release. The fixes' tests assert independent observations where each defect hid, rather than the component's own report of itself: the committed document against the rendered strip, the settled partition after a forced re-projection, the saved layout after a cold restore. And the release gate for this cut (#20022) adds a long serial run of the full Workstation and dashboard e2e directories on the cut candidate as complementary evidence beside the hosted tier and a manual walk; it does not recreate the five-minute session's conditions, and it is not a verdict on the detection design, which stays open on the Discussion. The first such runs found four failures the same files pass alone, all four traced rather than retried (see Honest bounds), and the hosted tier's two single-test failures of the week were traced from their retained Playwright traces: one to a grid measurement race that predates this window (#20028 → #20040), one to the maximized-pane flash (#20029 → #20031). Both are fixed here.
The day of the cut added a third thing. On the head with every fix in and the suites green, the operator's own walk found four defects in under an hour, all at gestures the suites did not perform: a drop on the pane's own edge, a drag far outside its strip, a drop from a popup into main after a mid-gesture tear-out, a popup's close. The engine's diagnostics named three of them before a line of source was read, the abandoned-gesture error on the console, the drag trace and the embodiment record, and the fourth was named by measuring the collapsed containers' styles; each fix carries the operator's gesture as a test. His final walk on the gated candidate found two more, the empty grid in a popup fixed the same evening with its gesture as a test, the capped title left open; the walk stays in the gate, and the detection design on the Discussion stays open.
▶️ What you can try
Open the Workstation in desktop Chrome:
- Click into the pane with many tabs and press its maximize action. Its tab strip now shows as many tabs as the maximized width holds, and the ⋯ overflow menu stays open until you pick a tab or dismiss it.
- Drag the third visible tab of that strip to the right and a little above the strip, then drop it. Every header stays inside the strip for the whole gesture.
- Pop a pane out past the window's edge, move a second tab into the popup, then drag it back over the main window and drop it. One header per tab, in both windows.
- Reload. The layout you built is the layout you get back.
- Pop a pane out, then press Undo. The pane comes home and the popup's window closes with it.
- Scroll the Live Event Stream grid down a few hundred pixels. It stays where you left it while the feed ticks.
⬆️ Upgrading from 13.3.0
Nothing to change. package.json's main, exports, bin, engines, type and dependencies are unchanged since 13.3.0; besides the version, it changes only the devDependency neo-agent-skills 0.1.55 → 0.1.56. The hover reveal is off by default (showOnHover: false on toolbars, headerActionsShowOnHover: false on dock Workspaces). Store#asContinuation(fn) is the one new method (#20059); the Store's other methods keep their behaviour.
Public behaviour to check:
| If your code… | In 13.3.1 | Do this |
|---|---|---|
reads Rectangle#intersects |
returns true/false for every input, including a zero-size rectangle; before, a zero-size rectangle could yield a non-Boolean (#20007) |
nothing, unless you compared with === against something other than a Boolean |
| drags tabs inside a popup window of a dock layout | the popup's strips honor dock zones and tear-out like the main window's (#20010) | nothing |
right-clicks a header whose columnMenu is off in a grid with menus |
a hint appears and the browser menu does not (#19960, #19959) | nothing |
| relies on a placement hint for a node that no longer exists | the layout still saves; the hint is dropped (#19995) | nothing |
| feeds a grid from a store that adds and trims records on a timer | an ordinary add or splice still resets the grid's scroll; a batch run inside store.asContinuation(() => { … }) keeps it (#20059) |
wrap a live feed's tick in asContinuation if your readers scroll |
closes a window by automation, with no beforeunload |
the window departs at its pagehide, so a popup's pane returns to the dock at once, with a history row (#20053) |
nothing |
sets hidden together with another config on a component, so its own update renders it away |
the component unmounts and mounts again when its parent shows it again; a promise asked while it is away settles then; any other way into a mounted state without a vnode is reported on console.error (#20044) |
read any new console.error that names one of your components |
| drags a tab in a dock strip far beyond the strip | its siblings rest until the pointer returns to the strip's release band; plain tab containers, maximized dock strips and the grid header keep reordering as before (#20074) | nothing |
| hides and shows a grid again, or moves it in the DOM | the grid gets its scroll position back on both axes, where the browser used to leave it at the origin (#20083) | nothing |
writes a config to a component after its destroy() |
the write lands on that instance's private copy of its VDOM template, never on the class template later instances clone (#20079) | nothing; the write is still dead work, so remove it when you find one |
🧭 What comes next
13.4's theme is set by the roadmap after this cut. Open from this window: the 100k Matrix trigger (#20009), the capped title that keeps its ellipsis (#20081), the root behind the post-destroy writes that #20079 contains, config controllers that outlive destroy() (#20080), a grid unit spec whose row-pool count depends on parallel load (#20067), the detection contract's graduation from Discussion #19994 into the e2e skill and fixtures, and whatever the standing watch leaves on #20022.
📦 Full Changelog
65 pull requests merged in neomjs/neo and 63 issues closed (63 as completed) between 13.3.0's release at 2026-10-21 10:40Z and dev at 36a74c743c, counted at 2026-10-23 22:09Z with GitHub search: is:pr is:merged base:dev merged:>=2026-10-21T10:40:08Z and is:issue is:closed closed:>=2026-10-21T10:40:08Z (reason:completed for the 63).
Workstation, docking and tabs (33)
- #19995 fix(dock): a placement hint naming a removed node no longer stops the layout auto-save · @neo-opus-ada
- #19996 fix(tab): the tab overflow partitions a maximized pane for its settled size · @neo-opus-ada
- #19997 fix(main): honor containing blocks in DomAccess#getClippedRect · @neo-gemini-julia
- #19998 fix(grid): a grid no ancestor bounds keeps a window-sized viewport and row pool · @neo-opus-grace
- #19999 fix(workstation): the grid column-drag proxy keeps the instrument skin · @neo-opus-vega
- #20000 fix(dashboard): suppress cross-zone tab drag while dock pane is maximized · @neo-gemini-julia
- #20001 fix(dock): a wrapped root edge-zone's rails frame the whole arrangement · @neo-opus-grace
- #20003 fix(dashboard): retire pre-detached source tab header on vessel transfer · @neo-gemini-maryam
- #20005 fix(draggable): exclude unrendered overflow items from SortZone drag snapshot · @neo-gemini-maryam
- #20008 fix(tab): a tab reorder no longer paints the headers the overflow withholds · @neo-opus-ada
- #20010 feat(workstation): a popup's tab strips get the main window's drag contract · @neo-opus-grace
- #20011 feat(dock): add option to reveal focus-gated header actions on hover · @neo-gemini-julia
- #20013 fix(dock): resolveFallbacks leaves a malformed fallbackTarget to validation · @neo-opus-ada
- #20016 fix(drag): a point inside the source's frame is the source's for the whole gesture · @neo-opus-grace
- #20019 fix(dock): a vessel is identified by its minted Group key, not its item · @neo-opus-grace
- #20030 fix(dock): retire a returning pane's pre-detached tab button · @neo-opus-ada
- #20031 fix(dock): a maximized pane stays maximized through a staged refresh · @neo-opus-ada
- #20032 fix(dock): a departed window takes no projection; the next mount lands it · @neo-opus-grace
- #20037 fix(workstation): a late-released tear-out mounts its vessel once · @neo-opus-grace
- #20049 fix(dock): an undo or redo keeps a popup's window in step with its document · @neo-opus-grace
- #20052 fix(toolbar): the overflow control opens its menu on the first press of an unfocused pane · @neo-fable-clio
- #20053 fix(main): a window closed without beforeunload departs at its pagehide · @neo-opus-ada
- #20063 fix(draggable): the drag-end focus restore waits for the dragged item's re-show to render · @neo-opus-grace
- #20071 fix(dock): a tab released on its own pane's edge lands the split the hover showed · @neo-opus-ada
- #20074 fix(dock): a tab dragged beyond its strip's release band leaves its siblings resting · @neo-opus-grace
- #20076 fix(dock): a tab dropped from a popup into main retires the header proxy its hover showed · @neo-opus-ada
- #20079 fix(component): a destroyed component's late writes stay off the vdom template later instances clone · @neo-opus-ada
- #19951 test(dock): the reorder arm travels only once its drag is armed · @neo-opus-ada
- #20015 test(dashboard): the maximized-strip stub binds resolveDraggedItemId · @neo-opus-grace
- #20021 test(e2e): settle tab-drag headers spec on post-drop overflow partition · @neo-gemini-julia
- #20039 test(e2e): hold the overflow-menu partition the menu opened against · @neo-opus-ada
- #20051 test(e2e): the no-flash reorder spec names a withheld header instead of counting frames · @neo-opus-vega
- #20069 test(e2e): the film-cursor oracle judges paint only on dots still in the document · @neo-opus-grace
Grid (11)
- #19956 fix(grid): the filter summary joins the search controls' row, so the first filter leaves the grid in place · @neo-opus-grace
- #19957 fix(grid): a header that scrolls itself into view brings its body along · @neo-opus-ada
- #19959 fix(grid): keep the browser menu away on a header arrow right-click · @Jah-yee
- #19960 feat(grid): show hint on right-click of menu-disabled headers · @neo-gemini-maryam
- #19962 fix(grid): a value picked before the menu's search focus lands keeps its focus · @neo-opus-ada
- #19968 fix(grid): wrap runtime columnMenu headers before syncing hint state · @neo-gemini-maryam
- #19978 fix(grid): a column drop's header scroll clamp leaves the grid scrolled · @neo-opus-ada
- #20040 fix(grid): a height reset retires the hidden projection it invalidates · @neo-opus-grace
- #20059 fix(data): a live feed's tick keeps the reader's scroll through Store#asContinuation · @neo-opus-grace
- #20060 fix(grid): a value menu's Clear hands focus to its search field before it hides · @neo-opus-ada
- #20083 fix(grid): a grid shown again or moved in the DOM gets its scroll position back · @neo-opus-ada
Engine core (10)
- #19946 fix(container): keep an item whose construction adds a sibling during createItems · @bercedev
- #19964 fix(vdom): a missing child in cn renders nothing instead of throwing · @neo-opus-grace
- #19967 fix(vdom): an update landing after unmount() leaves the children unmounted · @neo-opus-ada
- #19970 fix(vdom): an update landing after an ancestor's unmount() mounts no child · @neo-opus-ada
- #19974 fix(vdom): a hidden floating child is unmounted with the DOM its parent removes · @neo-opus-ada
- #20007 fix(util): Rectangle#intersects never returns a Boolean; add containsPoint() · @arnavsingh0418
- #20027 fix(worker): a departed window's key-less message reaches no other window · @neo-opus-grace
- #20042 fix(worker): main applies only the DOM updates of its own window · @neo-opus-ada
- #20044 fix(vdom): a component whose own flight renders it away unmounts · @neo-opus-ada
- #20055 test(unit): the canvas-transfer spec gives Neo.main.DomAccess back after its import · @neo-opus-ada
Build and contributor tooling (6)
- #19972 fix(build): recognize participle-lead and #N contributor claims · @neo-gemini-julia
- #19976 fix(build): preserve queued burst claims and refresh issue state · @neo-gemini-julia
- #20038 fix(ci): the CodeQL extraction guard waits out a trailing jobs view before reading a leg's log · @neo-opus-vega
- #20062 fix(ci): the mergeability controller leaves merged heads alone · @neo-opus-vega
- #20066 feat(ci): a green rerun repairs the same head's earlier red runs · @neo-opus-vega
- #20078 feat(ci): the e2e-brain shards split by measured time, not test count · @neo-opus-grace
Dependencies (5)
- #19979 fix(deps-dev): bump the all-deps group with 2 updates · Dependabot
- #19980 fix(deps-dev): bump neo-agent-skills from 0.1.55 to 0.1.56 · Dependabot
- #19981 fix(deps): bump the actions group with 2 updates · Dependabot
- #20034 fix(deps-dev): bump inquirer from 14.2.3 to 14.2.4 in the all-deps group · Dependabot
- #20036 fix(deps): bump the actions group with 2 updates · Dependabot
All changes delivered in 1 atomic commit: 24d865e