Skip to content

DO NOT MERGE — merge train: 3359,3306,3308,3358 - #3409

Closed
trinity-ability wants to merge 18 commits into
devfrom
train/20261008-1508
Closed

trinity-ability wants to merge 18 commits into
devfrom
train/20261008-1508

Conversation

@trinity-ability

Copy link
Copy Markdown
Contributor

Integration surface for #3359, #3306, #3308, #3358. Never merged; members merge individually once green.

alex-furman and others added 18 commits October 6, 2026 20:27
… a star that sticks on the first click (Abilityai/trinity-enterprise#841, #3356)

#3356 — the first star click looked lost because a chat-state read that was
already in flight (the 20 s poll, a turn finishing) answered with the pre-click
state and `refreshThreads` overwrote the optimistic star. The server had the
star; the screen did not; the second click re-sent the same PUT after the stale
read had gone by. Local star writes are now held over any read issued before
they settled (`createWriteGuard`). A starred, never-used Main is also listed in
the sidebar, so the header star on a fresh agent has a Starred row to show.

ent#841 — every chat tab but Main gets an × that ARCHIVES the chat (never
deletes): PUT/DELETE .../sessions/{id}/archive, one click, an Undo toast, an
InlineError under the strip on refusal. One archive rule (operator, 2026-10-08):
any archived chat, Reset's retired Main included, leaves the tab strip and the
sidebar's active groups and is listed under a collapsed Archived group; opening
it there reopens it. This reverses the ent#523 "becomes the newest tab" ruling.
The open chat's × is inert while its reply is in flight. Rooms are excluded:
the strip lists one agent's 1:1 chats, so a room has no tab. No schema change.

Also: a background chat with unread replies shows the sidebar row's blue count
on its tab (and in the "N more" menu); the strip is fixed-width, so nothing moves.

Fixes #3356

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e-button hover ground (+2 gray, Abilityai/trinity-enterprise#841)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ver share a Work card (#3357)

A running card showed "Steps could not be read right now." under its own
live activity line whenever its agent had a second run in flight.

- Card: stepsLine takes the card's live signal. While an activity line is
  on screen, an unknown read renders nothing (stages still render), so a
  run that visibly reports progress is never called unreadable.
- Source: an agent with several live runs used to be set to unknown
  without any read. It is now read once: an agent that publishes no
  pipeline reads none on every card; reported stages, which cannot be
  attributed to one run, stay unknown.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…widen to 192px (Abilityai/trinity-enterprise#841)

OverflowTabs gains an opt-in `starred` glyph (visible row, menu and mirror, in
the tab's own ink — no new colour). FIXED_TAB_WIDTH goes w-40 -> w-48 so the
star, the unread count and the close × fit beside the label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve tests/registry.json: rebuilt from the merge stages, keeping every
dev entry plus this PR's test_atomic_slot_admission.py entry (first).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tests/registry.json rebuilt from the merge stages: dev's entries plus this
branch's one new entry (unit/test_runtime_process_ownership.py).

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

dev's edge-case suites (#3327) pin that re-acquiring an already-held
execution id under the cap returns True and is counted once. The atomic
admission returned False, which CapacityManager reads as capacity-full
(queue_persistent would re-queue a running row; reject would fail it).

acquire_slot now returns True for a held id under the cap. The decision
stays inside the WATCH/MULTI/EXEC step: the held path EXECs a read-only
transaction, so 'held and under the cap' is validated against the same
watched keys as a new admission and retried on WatchError. Nothing is
written - the slot is not renewed and its metadata is not overwritten.
At the cap a held id still returns False (it counts itself), as on dev.

Tests:
- test_r14 (#3318) is fixed by this PR: drop the strict xfail and hook the
  pipeline's capacity read, so the interleaving still runs (and asserts it ran).
- test_atomic_slot_admission.py: duplicate-id cases updated to the idempotent
  contract (every caller True, one member, metadata untouched).

Refs #3305, #3318

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts the result (#3307)

test_headless_worker_finish_racing_waiter_cancellation_still_cleans_up
failed in CI (run 37567485053, Python 3.13.15) with
"500: Task execution error: '<' not supported between instances of
'NoneType' and 'int'" instead of CancelledError.

The test scheduled task.cancel() from the worker thread and let the worker
return immediately, assuming a cancel queued first is always processed before
the worker's result. Since CPython 3.13.12, asyncio's _chain_future copies the
result synchronously when the done-callback is attached on the loop thread to
an already-finished worker future, so run_in_executor hands back a completed
future, the caller never yields, the queued cancel never lands, and the stub
context goes through the real _finalize_headless_result (return_code None).
Product cleanup is correct in that ordering; the test's timing assumption was
not.

The worker now waits until the cancel has been delivered, and the loop is held
until the worker has fully returned, which pins the ordering the test names
(worker never observes the abandoned flag, caller cancelled before its result)
on every interpreter. No sleeps, no product change. Removing the caller's
finally: unregister still fails the test 10/10.

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

Every close shows the same words, so the toast's timer, restarted only by a
watch on the message text, never restarted for a second close: chat B's Undo
disappeared on chat A's timer. The restart is now keyed on a per-close
sequence number set where closeChat assigns undoClose.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on retry loop

Two findings from independent validation of the atomic admission.

1. A held id re-acquired under the cap now takes the same atomic write as a
   new admission, as dev does: ZADD re-anchors the score to now and the
   metadata hash + TTL are rewritten, inside the same WATCH/MULTI/EXEC. The
   previous read-only held path returned True on a lease dated from the
   original admission, so a re-dispatch could be reaped mid-run (the #2433
   class), and a held slot whose metadata had expired stayed without it. The
   member is still counted once; at the cap a held id still returns False and
   writes nothing (dev's contract, pinned by the p1 oracle).

2. The retry loop is bounded by MAX_ADMISSION_ATTEMPTS (32). redis-py reports
   a connection lost on a WATCHing pipeline as WatchError, so a connection
   that kept dying at EXEC retried forever, synchronously, on the event loop.
   Exhaustion raises SlotAdmissionError (a RedisError): an undecided admission
   never returns True or False.

Tests: the connection-loss cases inject the fault from the connection's send
inside the real Pipeline.execute (the old test replaced execute() wholesale
and asserted a ConnectionError production cannot raise from there); new cases
pin the bound, the single-loss retry, the refresh and the at-cap refusal.

Refs #3305

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	tests/registry.json
# Conflicts:
#	tests/registry.json
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.

4 participants