Skip to content

DO NOT MERGE — merge train: 3674,3665,3681,3682,3683 - #3685

Closed
vybe wants to merge 25 commits into
devfrom
train/20261013-2303
Closed

vybe wants to merge 25 commits into
devfrom
train/20261013-2303

Conversation

@vybe

@vybe vybe commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Integration surface for #3674, #3665, #3681, #3682, #3683. Never merged; members merge individually once green.

vybe and others added 25 commits October 13, 2026 08:42
Output of /release-plan (full mode) over v0.9.5..origin/dev (rc3, 15e3502):
payload census across both trackers, release-floor (ent#649) scoring,
five seam scans, the work order (6 MUST, 4 SHOULD), and draft
Upgrade-notes + Known-limitations blocks for /release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ze plan

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…6dde)

One enterprise commit: Abilityai/trinity-enterprise#853, the enterprise
half of Abilityai/trinity-enterprise#849 (the SSO setting "keep email-code
sign-in available for users"). The OSS half, #3428, is already on dev; this
bump ships the pair.

Related to Abilityai/trinity-enterprise#849

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (ent#856)

tabOrderFrom takes the arrival snapshot (Main first, recency, archived rows
ranked not pruned); agentChatTabs({order}) draws the strip in that frozen
order with born chats after Main by created_at; agentLanding gains arm 0
(most recent unread chat, via agentUnreadLanding) ahead of the last-open
arm; forgetLastOpen drops a closed chat from the session memory.
Requirement §5.43 added first. Shell wiring follows (A2).

Refs abilityai/trinity-enterprise#856

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…last-open (ent#856)

One watcher on the derived stageAgent (+ threadListTrusted) is the only
writer of the arrival snapshot tabOrder: take on a new agent, clear off
stage, retake once when an untaken (cold-link) snapshot becomes trusted.
The strip, cycleChat and closeChat's close-next all read the same order.
The /a/:name route watcher now passes lastOpen (landing arm 1) and
replaces the history entry it lands from, so Back does not loop.
forgetLastOpen runs on close and on a thread-missing 404.

Existing pins updated to the 2026-10-13 ruling: a route-driven landing
from a thread of the same agent returns to that thread (was: fresh), and
the openThread/landOnAgent signature pins carry the replace option.
Mutation: reverting openThread(row, { replace }) turns the Remount spec red.

Refs abilityai/trinity-enterprise#856

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… off stage (ent#856)

portalChatCycle: a reply that re-sorts the thread list mid-walk causes no
skip and no repeat; a snapshot for another agent is ignored.
portalAgentLandingStage: the snapshot is retaken on agent -> room -> agent
(sidebar door and by URL) and agent -> Inbox -> agent, held on a same-agent
click, and a cold link is untaken until trusted, retaken once, then held
through a later poll and a failed-then-recovered poll.

The room-by-URL case found a gap in A2: Back/forward or a link to
/workspace/r/:id never writes activeRoomId, so stageAgent kept the agent
on stage. stageAgent now also reads activeRoomIdFromRoute.
Mutations: dropping order from cycleChat, dropping the route-room term,
and retaking on every trusted flip each turn one of these red.

Refs abilityai/trinity-enterprise#856

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing note (ent#856)

workspace-chat-tabs-and-titles.md gains the section: the one stage-key
writer, the comparator, the unpruned snapshot, landing arms 0/1/fresh,
replace-on-route-landing, and the test map. §5.43 records the intended
arm-1 behaviour change (Back / the agent row from one of its chats returns
to the chat left open in this session — the 2026-10-13 landing ruling) and
that a room reached by URL is off stage.

Refs abilityai/trinity-enterprise#856

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…itch, T1/T2/T3/T5 recorded (ent#856)

Requirements first (RoE 1): the Action row of the §5.40 table reads Newest
by default with Urgency on the control; the new 'Action's sort' block
carries AC B1-B7 and the orchestrator rulings — T1 Action only, T2 the
operator queue left as is, T3 the rail follows the default explicitly,
T5 the three inconsistencies recorded (iii is a public bug for handoff).

Refs abilityai/trinity-enterprise#856

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…equest (pure layer) (ent#856)

portalInbox.js: INBOX_SORTS / INBOX_SORT_DEFAULT ('newest'),
normalizeInboxSort (allowlist), askRecencyCompare (created_at desc, id
desc — byAtDesc's rule), inboxSortFor(tab, query, stored) (URL, then the
viewer's memory, then Newest; null off Action), inboxSortKey /
loadInboxSort / saveInboxSort (identity-bucketed, try/catch, fail-open),
and actionItems(openAsks, now, sort). The urgency order (§3g C1) is
unchanged and now on the switch.
portalRailAsks.js: railAskItems passes INBOX_SORT_DEFAULT explicitly (T3).

RED first: portalInboxSort.spec.js (new) and the rail's T3 case failed on
the missing API and the urgency order. Existing pins moved to the ruling:
portalInboxActionSort passes 'urgency'; portalInbox.spec.js's default case
reads newest-first; the deep-linked-ask hold pin selects the older ask so
it still proves the second slot holds. Mutation: the rail passing
'urgency' turns two portalRailAsks cases red.

Refs abilityai/trinity-enterprise#856

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r memory (ent#856)

PortalInbox.vue: sort = inboxSortFor(tab, ?sort=, remembered) — the URL,
then the viewer's memory (bucketed by resolveLayoutIdentity), then Newest;
null off Action. actionItems takes it; visitKey carries it, so a change is
a new visit (rows re-sort on the click, then hold still). onSort writes
both the URL (replace) and the memory. A stray or unknown ?sort= leaves
the URL, like a stray ?from=. The control is BaseSelect variant="ghost"
("Sort asks": Newest / Urgency) in the list head line, Action only (T1).

PortalInboxList.vue: the head line is one fixed 44px box (the ghost
select's height) with a #head-end slot; the loading reserve draws the
same box, so neither a tab switch nor landing data moves the list. The
line grows from ~36px to 44px once, on every tab and state alike.

Mount pins (RED first, 7 failing): Newest default + the control's name and
options; switching writes ?sort= by replace and the memory; URL > memory >
default; another viewer does not inherit; unknown and off-Action ?sort=
stripped; a sort change is a new visit; priority + expiry marks in both
orders; one head-line box across states. Mutation: dropping sort from
visitKey turns two red.

Refs abilityai/trinity-enterprise#856

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
workspace-inbox.md: Action's order line reads Newest by default with
Urgency on the switch; a new section maps sort -> actionItems -> visitKey
-> onSort (URL replace + identity-keyed memory), the stray-key watcher,
the 44px head-line box, the rail's explicit default (T3) and the T2/T5
records; the test table gains the ent#856 specs.

Full npm run test:unit (--maxWorkers=2; the default worker count was
OOM-killed): 357 files, 5801 tests passed, ratchets included.

Refs abilityai/trinity-enterprise#856

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ent#856)

The shell skeleton (drawn by Portal.vue until the stage has a verdict)
still drew the old ~36px head line while PortalInbox's reserve and
PortalInboxList's ready head are the 44px h-11 box, so the first-paint
-> Inbox hand-off moved every row down 8px. The drift spec compared only
the header and tab strip; it now compares the head line against the
reserve's classes and the ready head's box too.

Red before the fix: 'the list head line is the Inbox's one 44px box'
(received 'shrink-0 px-4 pt-3 pb-2 ...').

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (ent#856)

Two immediate watchers each called replaceQuery in the same tick over the
same stale route.query, so the second replace put the first's key back
(?tab=unread&from=x&sort=newest ended as ?tab=unread&from=x). One watcher
now returns both stray keys and clears them in a single replace.

Red before the fix: 'a stray ?from= AND a stray ?sort= together both
leave the URL in one replace (review I2)' (received from: relay).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Full run-full.sh on a local stack (pytest-randomly installed, so test order
is shuffled) failed several tests that pass in isolation. None of these are
product bugs; each fix is test-side.

Integration/API tier:
- Default-state tests (Nevermined no-config x2, shared-folders defaults,
  read-only default) asserted "fresh agent" state on the module-scoped
  created_agent that sibling tests mutate. They now use isolated_agent.
- test_rename_agent_name_sanitization renamed to a fixed name. A deleted
  agent's soft-deleted ownership row keeps that name and its volume base
  reserved by design (#1664/#1671), so every run after the first got 409.
  The target is now derived from the fixture agent's unique suffix.
- TestSubscriptionAutoAssign round-robin/tiebreak: since #2409 auto-assign
  ranks by cached headroom, so a real operator subscription always beats the
  suite's fake tokens. Skip on foreign subscriptions like the existing
  no-subscription test (#2420); the tiebreak subs now carry the suite prefix.
- test_respond_to_pending_item: answering a platform-raised item lands
  `acknowledged` (#2372); pin both shapes.

Unit tier (fixed at the polluter, not the victim):
- test_brain_orb_postprocess: a lazily imported task_execution_service bound
  the patched database.db stub; pre-import it before each patch.
- test_monitoring_dormant_skip: bare sys.modules assignment of a stub
  services.monitoring_alerts; use monkeypatch.setitem.
- test_subscription_auto_switch_no_cred_import: restore the parent-package
  attribute as well as the sys.modules slot.
- test_ec_loop_service_edges m16: the [postgres] variant fails on the known
  float4 REAL-column bug, not pollution; strict xfail citing #3441.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l tabs, Asks always present (#3672, ent#892)

#3672 — the Workspace sidebar's agent and chat lists jumped on a cold load.
The roster, the chat list and the asks feed land at different times, and
each re-laid the list: agent rows re-sorted and grew a subtitle when chats
arrived, an asked agent was appended past the collapse limit when asks
arrived, chat groups popped in, and the Projects link (its capability rides
the roster) pushed the search box and both lists down.

- `sidebarSettled` (portalUtils, pure): bootstrap placed the caller AND the
  asks feed has a verdict (loaded / 404-403 / failed) — state, never a timer.
  Portal.vue latches it (`sidebarReady`), resetting only when the session ends.
- PortalSidebar: both sections hold skeleton rows (new PortalSkeleton
  `sidebar-agents` / `sidebar-chats` variants) until settled, then render in
  one pass inside one `.ws-fade-in` (180ms opacity, off under reduced motion).
  A platform session holds the Projects link's slot (`sidebar-link`). An empty
  chat list keeps a stable "No chats yet" state.
- Measured (Playwright, cold /workspace, 3 runs, fast and with staggered
  latency): CLS 0.008 -> 0.0003; the residual is in the Inbox pane header.

Mutation check: with PortalSidebar.vue reverted, 5 of the 11 tests in
portalSidebarCalmLoad.mount.spec.js go red; restored byte-identical.

trinity-enterprise#892 — the agent rail's tabs read Info, Canvas, Files,
Projects, Asks, Work, Loops. Asks is always present (operator ruling
2026-10-13): ent#836's presence rule and `asksTabPresent` are removed, the
registry entry declares an empty state ("No asks" / "Open the Inbox"), and
PortalRailAsks owns all four states via `railAsksView` (skeleton before a
verdict, LoadFailed, empty, rows). Default tab stays Work; a remembered tab
still wins; ⌥. walks the new order. With portalRail.js reverted, 5 of 14
portalRailAsks.spec.js tests go red.

Order-pin specs updated per the issue; two source-text pins moved to the new
sidebar gate; PortalSidebar's raw-gray ratchet entry lowered 35 -> 29.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, ent#894)

Carries #853 (f126dde), #876 SSO interactive-admin writes (c3c8005) and
#883 assignment/seat audit rows (a52ba5c).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The `sidebar-link` skeleton held a 36px slot for a platform session until
the sidebar settled, then vanished on every install not entitled to
Projects (all OSS installs) - a new layout shift to spare a late pop-in on
the few entitled ones. Operator ruling: no hold. The link renders once the
roster says `projectsAvailable`; the unused `sidebar-link` variant is
removed, the spec asserts nothing sits between New chat and the search box
before the verdict, and the feature flow no longer promises a held slot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the first `fetchAllSessions` fails, the store returns its last good
list - `[]` - and the sidebar told the person they had no chats. Portal.vue
now passes `:threads-failed="store.sessionsFailed"` (the same signal the
Inbox reads) to both sidebars. The empty copy renders only for a trusted
list; a failed read shows an InlineError instead (the stale banner over
rows, "Couldn't load your chats" over none) with a retry wired to
`refreshThreads`, dismissible and back on the next failure. Mount tests
cover all four cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PortalRailAsks read `view.state` but never `view.stale`, so a failed asks
refresh left the last list (or the empty state) on screen with no word of
it. It now raises the stale InlineError the way PortalRailCanvas,
PortalRailFiles and PortalLoops do ("Couldn't refresh your asks - showing
data from ..."), with a retry and a dismiss that re-arms on the next
failure. The two `./portalUtils` imports are merged into one. Mount tests
cover the banner over rows, over the empty state, and its absence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…3672)

Binding `:settled="true"` in Portal.vue left the suite green: the calm-load
spec mounts PortalSidebar alone. portalSidebarLatch.mount.spec.js mounts the
shell (shallow, the portalAgentLandingStage harness) with the asks read held
open and checks both sidebars hold `settled=false` until the verdict, latch
once, stay latched when the verdict's inputs drop on a later refresh, and
reset on sign-out so the next session's first load holds again.

Mutation check: `:settled="true"` -> 3 of 4 red; dropping the sign-out reset
-> 1 red; replacing the latch with a plain mirror -> 1 red; restored
byte-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ui PR touches the frontend UI — triggers Playwright e2e tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants