Repository navigation
Description
What is wrong
plugins/tree/src/allocate.js:118 refuses to group a burst when the first key of the profile is an area: one:
const key = burst && kindOf(first) !== 'area' && !(typeof parentRef === 'string' && parentRef.trim()) ? first : undefined
The refusal is deliberate (roles.js: area: is the bucket a role the vocabulary cannot place falls into, and a batch of unrelated Bots under one "General Lead" is not a group), but nothing pins it. Measured by Dev on f20cfa37: delete kindOf(first) !== 'area' and the four #810 burst tests stay green. The only red is #15 (3) "parallel create_child_bot never exceeds maxChildren", which fails because the root loses two children — an assertion about concurrency limits, not about inventing a group for an unplaceable role. So the suite does not state the intent, and a later refactor would remove the guard with an unrelated test as its only warning.
The failure direction is not silence: a burst of two Bots whose roles the vocabulary cannot place would get a lead keyed area:work / area:other — a synthetic "Work Lead" / "General Lead" holding Bots that share no role.
Expected behaviour
Two concurrent create_child_bot calls whose profiles have no project, no domain and no term (first key area:other or area:work) are placed on today's path: each under the caller (or its normal planned parent), and no Bot with a lead.key starting area: is created. Removing the area: guard must break a test that says exactly that.
Acceptance
- A new test in
plugins/tree/test/tree.test.js: twocreate_child_botcalls from one caller that are both in flight before either resolves, with names and roles the vocabulary cannot place (e.g. nameX, rolex, sokeysOf(profileFor(...))[0] === 'area:other'). After both settle: no Bot in the tree haslead.keystarting witharea:; both new Bots are children of the caller; neither sits under a newly created lead. - With
kindOf(first) !== 'area'removed, that test is red on its own assertions (a lead with anarea:key exists / a Bot is not under the caller). It must not be the unrelated#15 (3)maxChildren failure that carries the signal. - With the guard in place, the four existing burst tests,
#15 (3), andnpm testinplugins/treeare green. - No change to the burst window: one
setImmediateturn, seats taken before the decision — a call that starts after the decision still never joins.
Kill conditions
- A test that only passes because the generic maxChildren assertion happens to go red — the point is to say why the
area:key is not a group. - Pinning the guard by the lead's display name ("General Lead") instead of by
lead.key/ the child's parent. - Changing
roles.jssoxresolves to a domain: that hides the case instead of pinning it. - Widening the guard (e.g. also excluding
project:), which is a different design change.
Out of scope
- The other two
#810follow-ups (the empty section's wrapper border, the reused member counted in a group) — tracked separately. - The explicit-
parentopt-out and thelength < 2rule; both are already behavioural and tested.
Activity
[-]The #810 burst path has three unpinned edges: the exclusion, the archived section sitting above (not below) the reorg notice, and a duplicate 10 s roster fetch[/-][+]A burst whose first key is an `area:` bucket is refused by `allocate.js:118`, and no test pins that refusal[/+]Birfy commentedon Oct 23, 2026
Written-by: Dev
Claiming #816. Branch
fix/816-roster-feed-order, cut from3d062b9f.All three acceptance points go in one pull request, because they are one issue:
area:never groups a burst.allocate.jsalready refuses anarea:key as a burst key (a batch of Bots the vocabulary cannot place would otherwise be forced under one "General Lead"), but nothing pinned it: delete the clause and the suite stayed green. A concurrent burst of two unplaceable Bots must now leave both at the caller and create no lead.flybot.sidebar.sectionentries are registered atorder: 88(archived) andorder: 89(notice), and a slot renders ascending, so a smaller number is higher. The code was already right; the README did not say so. It does now, and a test pins the order so a later edit cannot silently flip it.registerStructureandmakeArchivedSectioneach polledctx.flybot.bots()on their own 10 s timer, so a mounted sidebar asked the host for the same list twice per tick. The plugin now keeps onerosterFeed: the first subscriber starts the poll, later ones join it without a read of their own,refresh()asks for an immediate read on a field change or a removal, and the last subscriber to leave stops the timer.fix(tree): read the roster once per tick, and pin the order and the a…
fix(tree): read the roster once per tick, and pin the order and the a…
Stop an empty sidebar section drawing a border (#818)