Skip to content

fix(map): a container pushes out what does not belong in it - #584

Merged
angel-manuel merged 4 commits into
devfrom
fix/live-map-containers-push-strays
Aug 28, 2026
Merged

fix(map): a container pushes out what does not belong in it#584
angel-manuel merged 4 commits into
devfrom
fix/live-map-containers-push-strays

Conversation

@angel-manuel

Copy link
Copy Markdown
Contributor

Stacked on #583. Its two commits show in this diff until it merges; the commit
to review here is the last one. Rebasing after #583 lands.

Two ways a container could tell a lie about membership.

A non-member could sit inside a box

Nothing stopped one. An org-level service whose ring slot happened to land over a
cluster, another user's agent that swung wide, the org aggregate — any of them
could come to rest inside a container. A box is a claim about who belongs to
whom, and that claim got sharper in #582, when services started joining
containers legitimately: "it is drawn inside the box" stopped being obviously
false for a service.

A stray is now pushed out by its nearest edge, caption included, since a label
lying across the edge makes the same claim the ball would. The stray takes the
push and the cluster takes a quarter of it back — all of it on the stray and a
node held to its ring target would shove forever; all of it on the cluster and
one loose ball could walk a whole fleet across the map.

Container-vs-container separation did not do what its comment said

It applied a flat force to every member, and the integrator divides force by
mass — so a mass-1 agent accelerated ten times harder than the mass-10 user it
orbits, and the cluster sheared instead of translating. pushCluster is
mass-scaled, which is what "push whole clusters" was always supposed to mean.

Neither force could win against the cooling

sp < 5 && am < 30 freezes a node outright, and at the alpha floor an overlap of
a few tens of units does not clear that bar. The push was real, and then the node
was frozen mid-overlap — which is why containers still visibly intersected. The
layout now stays awake while anything is overlapping and settles as soon as
nothing is, with an epsilon so a residual sliver does not keep the map shimmering.

Verification

Same fixture both times — five users with two agents each, four org-level
services on the shared ring, laid out and recentred — built from this commit's
parent and then from this commit.

Before

chi's container cuts through both dagmar.olsen's and bruno's, and bruno's
own caption sits across his box's left edge.

before

After

Three containers, three gaps. github stays outside on the ring.

after

Full frames: before · after

@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
overslash Ready Ready Preview Aug 28, 2026 10:55am

Request Review

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Code diff size

+101 / −12 across 1 file (net +89)

Area Files Added Removed Net
dashboard 1 +101 −12 +89

Source files under src/ only (.cjs, .go, .js, .jsx, .mjs, .py, .rs, .svelte, .ts, .tsx); test files and inline #[cfg(test)] modules excluded. 0 other changed files not counted.

Two ways a container could tell a lie about membership.

Nothing stopped a non-member from sitting inside a box. An org-level service
whose ring slot happened to land over a cluster, another user's agent that swung
wide, the org aggregate — any of them could come to rest inside a container, and
a box is a claim about who belongs to whom. That claim got sharper in #582, when
services started joining containers legitimately: "it is drawn inside the box"
stopped being obviously false for a service. A stray is now pushed out by the
nearest edge, caption included, since a label lying across the edge makes the
same claim the ball would. The stray takes the push and the cluster takes a
quarter of it back: all of it on the stray and a node held to its ring target
would shove forever, all of it on the cluster and one loose ball could walk a
whole fleet across the map.

Container-vs-container separation existed but did not do what its comment said.
It applied a flat force to every member, and the integrator divides force by
mass — so a mass-1 agent accelerated ten times harder than the mass-10 user it
orbits, and the cluster sheared instead of translating. It is mass-scaled now,
which is what "push whole clusters" was always supposed to mean.

Neither force could win against the cooling. `sp < 5 && am < 30` freezes a node
outright, and at the alpha floor an overlap of a few tens of units does not
clear that bar — the push was real, and then the node was frozen mid-overlap.
The layout now stays awake while anything is still overlapping and settles as
soon as nothing is, with a small epsilon so a residual sliver does not keep the
whole map shimmering.
Comment thread dashboard/src/lib/components/map/sim.ts Outdated
The stray loop measures `m` against a rect already inflated by `STRAY_GAP`, so
a node resting exactly at its clearance — nothing overlapping, everything where
it should be — still reported `m === STRAY_GAP`, which is over the epsilon that
keeps the layout above the cooling floor. One such node and the map never cools:
it re-warms itself every frame, forever, for a state that is already correct.

Report the penetration rather than the exit distance, the same convention the
box pair above uses when it reports `mag - BOX_GAP`. The force is unchanged and
was never wrong: it is proportional to the displacement needed to clear the gap,
which is exactly what the inflated rect measures.

Found by Seer on #584, which read the same line and diagnosed the force.
Comment thread dashboard/src/lib/components/map/sim.ts Outdated
`pushCluster` mass-scales, so what it takes is an acceleration, and dividing
that by the member count made a cluster progressively harder to shift the bigger
it got — a ten-node fleet reacting at a fortieth of the stray's push rather than
the quarter the line above it claims.

Backwards, because a big cluster is one with a big box, so it is exactly where a
stray is most likely to be stuck inside and where the reaction has work to do.
The reaction exists to break the deadlock when the stray and the cluster are
both held by their own ring targets; diluting it by size removed it from the
case it was written for. The box-vs-box path never did this — both clusters take
the same acceleration whatever their size.

A flat quarter now, which is what "the cluster takes a quarter of it back" says.

Found by Seer on #584.
Comment thread dashboard/src/lib/components/map/sim.ts
The comment added with the cooling fix said a stray "resting exactly at its
clearance still reports `m === STRAY_GAP`". It does not. At the clearance
`m` is zero — the guard skips that case before the arithmetic runs. `m` reaches
`STRAY_GAP` when the stray is *touching* the box with nothing overlapping.

The code was right either way; the comment named the wrong configuration, and
described a phantom force at a boundary where in fact there is none. Both places
`m` is used now say which quantity they want and why they differ: the force
wants the distance still needed to reach the clearance, the cooling flag wants
the penetration, and touching the box is a full push for the first and nothing
at all for the second.

No behaviour change.
Comment thread dashboard/src/lib/components/map/sim.ts
@angel-manuel
angel-manuel merged commit 5a22804 into dev Aug 28, 2026
14 checks passed
@angel-manuel
angel-manuel deleted the fix/live-map-containers-push-strays branch August 28, 2026 11:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant