Tags · Thurbeen/thurbox · GitHub
Skip to content

Tags: Thurbeen/thurbox

Tags

v2.76.2

Toggle v2.76.2's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(deps): update rust crate tokio to v1.54.1 (#1612)

This PR contains the following updates:

| Package | Type | Update | Change | OpenSSF |
|---|---|---|---|---|
| [tokio](https://tokio.rs)
([source](https://redirect.github.com/tokio-rs/tokio)) | dependencies |
minor | `1.53.3` → `1.54.1` | [![OpenSSF
Scorecard](https://api.securityscorecards.dev/projects/github.com/tokio-rs/tokio/badge)](https://securityscorecards.dev/viewer/?uri=github.com/tokio-rs/tokio)
|

---

### Release Notes

<details>
<summary>tokio-rs/tokio (tokio)</summary>

###
[`v1.54.1`](https://redirect.github.com/tokio-rs/tokio/releases/tag/tokio-1.54.1):
Tokio v1.54.1

[Compare
Source](https://redirect.github.com/tokio-rs/tokio/compare/tokio-1.54.0...tokio-1.54.1)

### 1.54.1 (October 21st, 2026)

##### Fixed

- time: revert "remove the runtime handle from timers" ([#&#8203;8692])

[#&#8203;8692]: https://redirect.github.com/tokio-rs/tokio/pull/8692

###
[`v1.54.0`](https://redirect.github.com/tokio-rs/tokio/releases/tag/tokio-1.54.0):
Tokio v1.54.0

[Compare
Source](https://redirect.github.com/tokio-rs/tokio/compare/tokio-1.53.3...tokio-1.54.0)

### 1.54.0 (October 19th, 2026)

The 1.54.0 release includes all changes up to 1.53.3.

The MSRV is increased to 1.85. ([#&#8203;8514])

##### Added

- fs: add `File::set_times`, `File::set_modified`, and `fs::FileTimes`
([#&#8203;8504])
- net: implement `Default` for `named_pipe::{ClientOptions,
ServerOptions}` ([#&#8203;8378])
- runtime: add `Builder::max_io_events_per_busy_tick` ([#&#8203;8410])
- runtime: stabilize `tokio::runtime::worker_index()` ([#&#8203;8431])
- sync: add `SemaphorePermit::semaphore()` ([#&#8203;8482])
- sync: add `blocking_acquire` methods to `Semaphore` ([#&#8203;8269])
- sync: add `watch::Receiver::is_closed` ([#&#8203;8481])
- wasm: support JSPI for Emscripten target ([#&#8203;8285],
[#&#8203;8627])
- wasm: support the `wasm32-unknown-emscripten` target ([#&#8203;8281],
[#&#8203;8422], [#&#8203;8528], [#&#8203;8628])

##### Changed

- fs: reject zero in `File::set_max_buf_size` ([#&#8203;8449])
- io: make `copy_buf` cooperative ([#&#8203;8459])
- io: panic when `BufReader` or `BufStream` is created with zero
capacity ([#&#8203;8494])
- io: panic when in-memory pipe is created with zero capacity
([#&#8203;8397])
- io: reject zero buffer size in `copy_bidirectional_with_sizes`
([#&#8203;8454])
- io: support vectored writes in `Sink` ([#&#8203;8663])
- net: start `UnixStream::pair` and `UnixDatagram::pair` sockets as
writable ([#&#8203;8444])
- net: start accepted sockets as readable and writable ([#&#8203;8435])
- net: support `UCred` on Fuchsia ([#&#8203;8257])
- process: use `mio::unix::pipe` for Unix child stdio ([#&#8203;8603])
- runtime: cache-pad the shared injection queue and idle state
([#&#8203;8622])
- runtime: decide auto-boxing of spawned futures at monomorphization
time ([#&#8203;8414])
- runtime: make `enable_io` available whenever `rt` is enabled
([#&#8203;8387])
- runtime: move `block_in_place` setup into a non-generic function
([#&#8203;8315])
- runtime: move the multi-thread inject queue to its own mutex
([#&#8203;8382])
- runtime: panic when `max_io_events_per_tick` is zero ([#&#8203;8476])
- runtime: suggest `.await` in nested `block_on` panic message
([#&#8203;8501])
- sync: avoid atomic reader count in broadcast slots ([#&#8203;8357])
- sync: use acquire/release orderings in `Notify` ([#&#8203;8325])
- time: remove the runtime handle from timers ([#&#8203;8458])

##### Fixed

- fs: restore `File` internal state when op fails ([#&#8203;8291])
- io: complete zero-length memory stream operations immediately
([#&#8203;8323])
- io: flush the writer when the reader stalls in `copy_buf`
([#&#8203;8424])
- io: keep the shutdown bit when clearing readiness ([#&#8203;8435])
- io: preserve the full `ScheduledIo` tick in readiness events
([#&#8203;8331])
- io: read lines as bytes so a partial line survives an I/O error
([#&#8203;8400])
- io: retry on `ErrorKind::Interrupted` in more I/O operations
([#&#8203;8417], [#&#8203;8432], [#&#8203;8448], [#&#8203;8453],
[#&#8203;8455], [#&#8203;8460])
- io: use `spawn_mandatory_blocking` for stdout and stderr
([#&#8203;8506])
- io: wait for pending operations before `BufReader` seeks
([#&#8203;8599])
- net: close `CreateFileW` handle on `SetNamedPipeHandleState` failure
([#&#8203;8418])
- net: fix peer credentials on NetBSD ([#&#8203;8589])
- net: saturate `TcpSocket` and `UnixSocket` listen backlog
([#&#8203;8511])
- net: treat `ENOTCONN` as success in `UnixStream::shutdown`
([#&#8203;8526])
- runtime: allow building with `--all-features` without `tokio_unstable`
([#&#8203;8401])
- runtime: avoid overflow in `shutdown_timeout()` ([#&#8203;8497])
- runtime: fix `spawn_blocking` hang when only scheduler workers exist
([#&#8203;8408])
- runtime: fix misleading signal driver panic message ([#&#8203;8419])
- sync: preserve FIFO lock order when coop budget is exhausted
([#&#8203;8371])
- sync: use `usize` for permit count in `SemaphorePermit`
([#&#8203;8405])
- time: avoid interval deadline overflow ([#&#8203;8385])
- time: handle wrapped top-level timer-wheel slots ([#&#8203;8334])

##### Unstable

- fs: fall back when `io_uring` has no IO driver ([#&#8203;8165])
- metrics: cap log histogram precision ([#&#8203;8496])
- runtime: add an opt-in sharded `spawn_blocking` queue ([#&#8203;8337])
- runtime: expose schedule latency in task hooks ([#&#8203;8282])
- taskdump: fix idle bookkeeping when a task dump wakes parked workers
([#&#8203;8372])
- taskdump: skip non-idle tasks during taskdumps ([#&#8203;8568])

##### Documented

- docs: fix stray braces and broken links in rustdoc ([#&#8203;8605])
- fs: document cancellation behavior of `tokio::fs` ([#&#8203;8265])
- net: add Safety docs for named pipe `from_raw_handle` ([#&#8203;8378])
- net: document that `TcpStream::connect` returns only the last error
([#&#8203;8421])
- runtime: add error documentation for `runtime::Builder`
([#&#8203;8297])
- runtime: link `available_parallelism` in worker count docs
([#&#8203;8625])
- sync: clarify panic behavior of `broadcast::channel()`
([#&#8203;8420])
- sync: fix typo in `Semaphore` documentation ([#&#8203;8521])

[#&#8203;8165]: https://redirect.github.com/tokio-rs/tokio/pull/8165

[#&#8203;8257]: https://redirect.github.com/tokio-rs/tokio/pull/8257

[#&#8203;8265]: https://redirect.github.com/tokio-rs/tokio/pull/8265

[#&#8203;8269]: https://redirect.github.com/tokio-rs/tokio/pull/8269

[#&#8203;8281]: https://redirect.github.com/tokio-rs/tokio/pull/8281

[#&#8203;8282]: https://redirect.github.com/tokio-rs/tokio/pull/8282

[#&#8203;8285]: https://redirect.github.com/tokio-rs/tokio/pull/8285

[#&#8203;8291]: https://redirect.github.com/tokio-rs/tokio/pull/8291

[#&#8203;8297]: https://redirect.github.com/tokio-rs/tokio/pull/8297

[#&#8203;8315]: https://redirect.github.com/tokio-rs/tokio/pull/8315

[#&#8203;8323]: https://redirect.github.com/tokio-rs/tokio/pull/8323

[#&#8203;8325]: https://redirect.github.com/tokio-rs/tokio/pull/8325

[#&#8203;8331]: https://redirect.github.com/tokio-rs/tokio/pull/8331

[#&#8203;8334]: https://redirect.github.com/tokio-rs/tokio/pull/8334

[#&#8203;8337]: https://redirect.github.com/tokio-rs/tokio/pull/8337

[#&#8203;8357]: https://redirect.github.com/tokio-rs/tokio/pull/8357

[#&#8203;8371]: https://redirect.github.com/tokio-rs/tokio/pull/8371

[#&#8203;8372]: https://redirect.github.com/tokio-rs/tokio/pull/8372

[#&#8203;8378]: https://redirect.github.com/tokio-rs/tokio/pull/8378

[#&#8203;8382]: https://redirect.github.com/tokio-rs/tokio/pull/8382

[#&#8203;8385]: https://redirect.github.com/tokio-rs/tokio/pull/8385

[#&#8203;8387]: https://redirect.github.com/tokio-rs/tokio/pull/8387

[#&#8203;8397]: https://redirect.github.com/tokio-rs/tokio/pull/8397

[#&#8203;8400]: https://redirect.github.com/tokio-rs/tokio/pull/8400

[#&#8203;8401]: https://redirect.github.com/tokio-rs/tokio/pull/8401

[#&#8203;8405]: https://redirect.github.com/tokio-rs/tokio/pull/8405

[#&#8203;8408]: https://redirect.github.com/tokio-rs/tokio/pull/8408

[#&#8203;8410]: https://redirect.github.com/tokio-rs/tokio/pull/8410

[#&#8203;8414]: https://redirect.github.com/tokio-rs/tokio/pull/8414

[#&#8203;8417]: https://redirect.github.com/tokio-rs/tokio/pull/8417

[#&#8203;8418]: https://redirect.github.com/tokio-rs/tokio/pull/8418

[#&#8203;8419]: https://redirect.github.com/tokio-rs/tokio/pull/8419

[#&#8203;8420]: https://redirect.github.com/tokio-rs/tokio/pull/8420

[#&#8203;8421]: https://redirect.github.com/tokio-rs/tokio/pull/8421

[#&#8203;8422]: https://redirect.github.com/tokio-rs/tokio/pull/8422

[#&#8203;8424]: https://redirect.github.com/tokio-rs/tokio/pull/8424

[#&#8203;8431]: https://redirect.github.com/tokio-rs/tokio/pull/8431

[#&#8203;8432]: https://redirect.github.com/tokio-rs/tokio/pull/8432

[#&#8203;8435]: https://redirect.github.com/tokio-rs/tokio/pull/8435

[#&#8203;8444]: https://redirect.github.com/tokio-rs/tokio/pull/8444

[#&#8203;8448]: https://redirect.github.com/tokio-rs/tokio/pull/8448

[#&#8203;8449]: https://redirect.github.com/tokio-rs/tokio/pull/8449

[#&#8203;8453]: https://redirect.github.com/tokio-rs/tokio/pull/8453

[#&#8203;8454]: https://redirect.github.com/tokio-rs/tokio/pull/8454

[#&#8203;8455]: https://redirect.github.com/tokio-rs/tokio/pull/8455

[#&#8203;8458]: https://redirect.github.com/tokio-rs/tokio/pull/8458

[#&#8203;8459]: https://redirect.github.com/tokio-rs/tokio/pull/8459

[#&#8203;8460]: https://redirect.github.com/tokio-rs/tokio/pull/8460

[#&#8203;8476]: https://redirect.github.com/tokio-rs/tokio/pull/8476

[#&#8203;8481]: https://redirect.github.com/tokio-rs/tokio/pull/8481

[#&#8203;8482]: https://redirect.github.com/tokio-rs/tokio/pull/8482

[#&#8203;8494]: https://redirect.github.com/tokio-rs/tokio/pull/8494

[#&#8203;8496]: https://redirect.github.com/tokio-rs/tokio/pull/8496

[#&#8203;8497]: https://redirect.github.com/tokio-rs/tokio/pull/8497

[#&#8203;8501]: https://redirect.github.com/tokio-rs/tokio/pull/8501

[#&#8203;8504]: https://redirect.github.com/tokio-rs/tokio/pull/8504

[#&#8203;8506]: https://redirect.github.com/tokio-rs/tokio/pull/8506

[#&#8203;8511]: https://redirect.github.com/tokio-rs/tokio/pull/8511

[#&#8203;8514]: https://redirect.github.com/tokio-rs/tokio/pull/8514

[#&#8203;8521]: https://redirect.github.com/tokio-rs/tokio/pull/8521

[#&#8203;8526]: https://redirect.github.com/tokio-rs/tokio/pull/8526

[#&#8203;8528]: https://redirect.github.com/tokio-rs/tokio/pull/8528

[#&#8203;8568]: https://redirect.github.com/tokio-rs/tokio/pull/8568

[#&#8203;8589]: https://redirect.github.com/tokio-rs/tokio/pull/8589

[#&#8203;8599]: https://redirect.github.com/tokio-rs/tokio/pull/8599

[#&#8203;8603]: https://redirect.github.com/tokio-rs/tokio/pull/8603

[#&#8203;8605]: https://redirect.github.com/tokio-rs/tokio/pull/8605

[#&#8203;8622]: https://redirect.github.com/tokio-rs/tokio/pull/8622

[#&#8203;8625]: https://redirect.github.com/tokio-rs/tokio/pull/8625

[#&#8203;8627]: https://redirect.github.com/tokio-rs/tokio/pull/8627

[#&#8203;8628]: https://redirect.github.com/tokio-rs/tokio/pull/8628

[#&#8203;8663]: https://redirect.github.com/tokio-rs/tokio/pull/8663

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/Thurbeen/thurbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xODguMiIsInVwZGF0ZWRJblZlciI6IjQ0LjE4OC4yIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

v2.76.1

Toggle v2.76.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(core): resume a custom-named Claude launcher's conversation on re…

…start (#1606)

Fixes #1603

## Intent

A Claude agent registered under a custom name, launched through a
wrapper script that sets its own `CLAUDE_CONFIG_DIR`, keeps its
transcripts where thurbox's resume check cannot look. Restart therefore
relaunched `--session-id <id>` onto a conversation that already existed.
Claude refuses that with "Session ID … is already in use", and the pane
dies. Every restart and restore of such a session hit it, including
`session restart`.

## What Changed

- On a turn-start signal (`THURBOX_SIGNAL_TURN=start`, set by Claude's
`UserPromptSubmit` hook), `session signal` reads the hook's stdin
payload. When the payload's `session_id` matches the row's agent session
id, it records that id as `thurbox.conversation_started` session meta.
- Restart resumes when that marker matches the row's id, and otherwise
falls back to the existing transcript and `resume_latest` logic. The
decision now rests on the agent's own report, not on guessing a config
directory.
- The marker requires the agent to name the id. A forked Claude
conversation runs under an id Claude chose, so it records nothing and
keeps the old behaviour.
- `session restart` help, `docs/AGENTS.md`, `docs/ARCHITECTURE.md` and
the `thurbox-agents` skill now describe the rule.

```mermaid
flowchart LR
  H[UserPromptSubmit hook] -->|payload session_id == row id| M[(thurbox.conversation_started)]
  R[restart] --> Q{marker == row id?}
  Q -->|yes| RES[--resume id]
  Q -->|no| T[transcript / resume_latest check, unchanged]
```

## Risk Assessment

- Existing sessions gain the marker only at their next turn after
upgrading. Until then they keep the previous behaviour, so a custom
launcher that hides its config directory still needs that one turn
first.
- Reading stdin only happens on a turn-start signal and never from a
terminal. Input is capped at 16 MiB, and anything unparseable records
nothing.
- Hand-respawned panes that lost `THURBOX_SESSION` cannot report at all,
and that remains out of scope. Pinning `CLAUDE_CONFIG_DIR` in the
agent's `env`, the documented multi-account shape, still works as
before.

## Testing

- New end-to-end regression
`create_e2e::a_wrapped_claude_resumes_the_conversation_its_hook_reported`
spawns a custom-named wrapper agent on a real tmux server. Its
transcript sits in a private config dir, and the turn-start hook fires
from inside the pane with a Claude-shaped payload. The test asserts that
`session restart` relaunches `--resume <id>`. On `origin/main` it failed
with `--session-id <id>`, the reported defect. It also asserts that a
payload naming a different (forked) id records nothing; disabling that
check makes the test fail.
- Review: two rounds. Round one found the fork case above (a fork would
have been resumed under an id Claude never had), which was fixed
test-first. Round two found nothing.
- Gate from `.publish.yaml`: `just lint` and `RUSTDOCFLAGS="-D warnings"
cargo doc --no-deps --all-features` passed. In `cargo nextest run
--all`, 4457 of 4471 tests passed. Eight failures came from running two
gates concurrently on WSL and pass in isolation. Six fail identically on
`origin/main` (086218f) on this machine, so they predate this change:
`kernel::diff::tests::requesting_marks_it_pending_immediately`,
`create_e2e::failed_launch_rolls_back_its_worktree_and_allows_the_same_branch_retry`,
two `event_consumers_e2e` pre-launch tests and two `tui_e2e` pty tests.
CI is the authority on those.
- Not verified: a real `claude` restart through a real wrapper; the
stand-in reproduces the arguments and the hook.

— LeTuR's agent
<!-- fleet:worker u2137-3bb27005bfe0b988 -->

v2.76.0

Toggle v2.76.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat(cli): report whether a session is ready for input (#1607)

Fixes #1604

## Intent

Callers that hand a session its first prompt must know whether the agent
can take input yet, and thurbox owns the agent runtimes. Until now a
caller pieced that together from `state`, and for a fresh agent that
fires no hook before its first turn (Codex) it had to carry its own
screen probe per agent. Fleet's `initial_codex_ready` is the example.
This PR adds one agent-agnostic `ready` field so callers no longer need
per-agent knowledge.

## What Changed

- `session get` and `session list` now carry `ready`
(`Assessment::ready`):
- `true`: the hook's last report is `idle` or `done`; or no hook has
reported, an agent holds the pane, and its input line shows a drawn,
empty composer (one prompt glyph left of the cursor).
- `false`: `working`, `blocked` or `background`; parked; a pane with no
agent; a contradicted report; or a fresh agent whose composer is not
drawn yet.
- `null`: nothing can say, for example the pane was not probed or its
input line was unreadable. Callers should ask again.
- The input line is read only for a `running` session with no hook
report, and only by a verb that probes the pane. A reported session
costs no extra read.
- `composer::shows_prompt` is the stricter twin of `classify`'s `Empty`:
a blank line, which is what a booting pane shows, is not a prompt.
- The contract is documented in `docs/ORCHESTRATION.md` → Readiness, the
`session get` help and the `thurbox-session-status` skill.

## Risk Assessment

- The field is additive; existing fields keep their values. A peer
reading the mirror shape ignores it.
- The pre-hook answer is a screen reading. Holding text, a dialog with a
hidden cursor (Codex's update and folder-trust prompts) and a blank
booting screen all read `false`. An agent whose composer prompt is not
one of the known glyphs (`❯ › > → » $ % #`) would stay `false` until its
first hook.
- `session list` without `--verify` answers `null` for a never-reported
session, because it does not probe panes.

## Testing

- New end-to-end regression
`cli_session_hook_state::a_session_is_ready_once_its_composer_is_drawn_and_then_as_its_hooks_say`.
A stand-in Codex runs on a real tmux server: it boots (`ready: false`),
then draws `› ` with a placeholder right of the cursor (`ready: true`,
`hook_reported: false`); then `working` gives `false` and `idle` gives
`true`. On `origin/main` it failed with `ready: null` (no such answer).
A unit test pins `shows_prompt`.
- Checked live against the installed CLIs on a private tmux socket, with
no input sent: Claude's empty composer reads `❯` left of the cursor,
which counts as ready. Codex's update and folder-trust dialogs (hidden
cursor, nothing left of it) read not ready. Codex's composer itself was
not reached, because every folder tried needed a trust decision that
would have saved config. Its shape (cursor at column 2 after `› `) comes
from Fleet's existing probe and is not verified here.
- Review: one round, no findings.
- Gate from `.publish.yaml`: `just lint` and `RUSTDOCFLAGS="-D warnings"
cargo doc --no-deps --all-features` passed. In `cargo nextest run
--all`, 4465 of 4472 tests passed. One failure was a load flake and
passes in isolation. Six fail identically on `origin/main` (086218f) on
this machine, so they predate this change:
`kernel::diff::tests::requesting_marks_it_pending_immediately`,
`create_e2e::failed_launch_rolls_back_its_worktree_and_allows_the_same_branch_retry`,
two `event_consumers_e2e` pre-launch tests and two `tui_e2e` pty tests.

— LeTuR's agent
<!-- fleet:worker u2137-3bb27005bfe0b988 -->

v2.75.5

Toggle v2.75.5's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(git): delete the branch of a worktree thurbox created on teardown (…

…#1600)

## Problem

When thurbox tears down a worktree it created (`created_by_thurbox`), it
runs `git worktree remove --force` but never deletes the branch it cut
with `--worktree-branch`. Every deleted session left its local branch
behind.

## Change

Once a thurbox-created worktree has been removed, teardown also deletes
its **local** branch, on the same host (local, ssh or WSL):

- `git::delete_created_branch_on` runs `git worktree prune` first (git
refuses `branch -D` on a branch that a registered but vanished worktree
still has checked out), then `git branch -D`.
- `-D`, not `-d`: a squash-merged branch is never an ancestor of its
base. Unpushed commits go with it, in the same way `worktree remove
--force` already discards uncommitted work.
- It is idempotent. If the branch is already gone, that's a no-op and
not an error. A worktree whose directory vanished but is still
registered gets pruned, then its branch is deleted.
- It is best-effort. Results land in the delete report as
`deleted_branches` / `branch_errors` (CLI JSON and human output). A
branch error does **not** count as owed teardown; the checkout is
already gone.
- Remote branches are never touched.

Where it happens:
- `delete.rs` `remove_worktree_into`: runs after a successful removal,
or when the worktree is already unregistered or prunable. This covers
immediate force delete, the overdue reap, the owed-teardown retry and
the purge of a soft-deleted session.
- `reconcile.rs` creation rollback: deletes the branch a failed
**spawn** cut. It never does this for a **restore** rollback, because
there the branch is the soft-deleted session's own work.

Not touched:
- Worktrees the session merely opened (`kept_worktrees`).
- Anything during a **soft delete**: the worktree and branch stay for
undo, and go when the session is purged.
- A branch another session still names: a live one, or an unpurged
soft-deleted one that isn't force-deleted. A fork inherits its parent's
checkout, so without this guard a parent's hard delete would destroy the
branch a soft-deleted fork's undo needs.
`create_e2e::a_saved_fork_recovers_its_checkout_after_parent_teardown`
caught this.

## Decision: who owns a branch `create_worktree_on` attached to

`create_worktree_on` (and so `create_or_attach_worktree`) attaches to a
branch that already exists instead of failing, and the session still
records `created_by_thurbox: true`. That flag describes the
**directory**, not the branch. **Decision: thurbox owns only branches it
cut with `-b`.** A `--worktree-branch` that names a branch the user
already had must never delete that branch.

The database can't tell the two cases apart, so creation records
ownership in the repository: after `worktree add -b`, it sets
`branch.<name>.thurboxCreated = true` in the repo's git config. Teardown
deletes only marked branches. This needs no schema change, works the
same on remote hosts (it is just another `git` call through the host
launcher), and `git branch -D` removes the branch's config section, so
the mark goes with the branch it vouches for. A branch a rolled-back
attempt left behind keeps its mark, so a retry that attaches to it still
owns it.

Consequences worth knowing:
- Branches thurbox created **before this change** have no mark, so they
are kept, as today. Only sessions created from now on get cleanup.
That's fine for Fleet, which will pass `--worktree-branch` for new
tasks.
- **Best-effort restore of a force-deleted session** used to reattach
the surviving branch. For a thurbox-cut branch there's now nothing to
reattach: `recreate_worktrees` skips it and `worktrees_recovered`
reports 0. This follows from the force-delete decision (unpushed commits
go with it). Soft delete is the undoable path. Docs and the thurbox-cli
skill now say so.

## Docs

`docs/FEATURES.md`, `docs/ARCHITECTURE.md` (ADR-26 rollback sentence),
`.agents/skills/thurbox-cli/SKILL.md`, and the rationale comment on the
delete confirmation in `ui/plugins/10_sessions.lua`. That comment
claimed force-delete "leaves the branch"; the confirmation policy itself
is unchanged, because unpushed commits already trigger the question.

## Tests

- `src/git/tests.rs`: created branch deleted after its worktree goes
(and a second call is a no-op); a branch with unmerged commits is still
deleted (`-D`); an attached pre-existing branch is unmarked and kept; a
vanished-but-registered worktree is pruned and its branch deleted; a
branch still checked out errors instead of being deleted.
- `src/session_ops/delete.rs`: hard teardown deletes the branch; the
gone-directory case; branches not created by thurbox are kept (attached
branch, and an opened worktree); soft delete keeps both worktree and
branch.
- `tests/create_e2e.rs`: a failed spawn's rollback leaves no branch, and
force-deleting the retried session reports and deletes it.

## Checks

- `cargo fmt --all -- --check`: clean.
- `cargo clippy --all-targets --all-features -- -D warnings`: clean on
this change. The local toolchain (1.96) also flags 3 pre-existing
`nonminimal_bool` asserts in `src/kernel/registry.rs`, which this PR
doesn't touch.
- `RUSTDOCFLAGS="-D warnings" cargo doc`, `cargo test --test
architecture_rules`: pass.
- `just test`: 4371 passed, 79 failed. All the failures are tmux/pty e2e
harnesses (`tui_e2e`, `backend_contract`, `one_window_per_stamp`, …) and
fail the same way on unmodified `main` on this machine. 76 of 77 re-ran
identically on a baseline checkout; the remaining one,
`a_search_result_is_revealed_as_soon_as_it_is_the_selected_one`, is
flaky on both. Every test touching this change passes.
- `rumdl check .`: 5 pre-existing findings in files this PR doesn't
touch.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

v2.75.4

Toggle v2.75.4's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(core): pin interactive Bash in Nix tests (#1599)

## Intent

Restore the pinned Nix development shell's real-terminal test contract.
The shell supplied stdenv's non-interactive Bash, so the fixtures had no
Readline `bind` builtin or bracketed-paste negotiation even though they
launch Bash interactively. This blocked canonical verification
associated with #1467; it is not a production input-path change.

## What Changed

- Add `bashInteractive` to the Nix development shell.
- Add a real-tmux regression test that proves the Bash fixture has
Readline bracketed paste enabled.
- Isolate real-shell fixtures from an inherited `INPUTRC` so personal
Readline settings cannot change the security precondition.
- Document the shell requirement in the development guide and testing
skill.

The failing-first test reproduced `bash: bind: command not found` under
the pinned shell. With only interactive Bash substituted while retaining
pinned Rust 1.98.1, tmux 3.7c, Bash 5.3.15, and `TERM=xterm-256color`,
it passed. The five terminal-input failures that motivated this
prerequisite then passed together, including the malicious pasted
end-marker assertion.

## Risk Assessment

Low. The change affects development-shell composition and its tests
only; production Rust behavior is unchanged. The added Bash package is
from the already locked nixpkgs revision, not a dependency update. This
is not visually observable, so there is no UI evidence to capture.

One first full run used an overly long private `TMPDIR` and
independently hit Unix `SUN_LEN` for per-test UI sockets. The same
focused UI test failed under that prefix and passed under the short
private `/tmp/tbx-u94`; the unchanged full gate was then run there. The
shared Fleet target and live tmux sessions were not touched.

## Testing

- `nix develop --command just lint`
- `nix develop --command env RUSTDOCFLAGS="-D warnings" cargo doc
--no-deps --all-features`
- `TMPDIR=/tmp/tbx-u94 nix develop --command cargo nextest run --all` —
4,460 passed, 4 skipped
- Regression red: `the_interactive_bash_fixture_enables_bracketed_paste`
failed because pinned stdenv Bash had no `bind`
- Regression green: the same test passed with locked `bashInteractive`
- Hostile `INPUTRC`: the regression and five original focused failures
passed 6/6, including the malicious end-marker case
- Publish review: two rounds; one inherited-`INPUTRC` finding reproduced
and fixed
- Independent review: no findings at exact head
`6412eeb94eed4c53a65407d33286fce0cc8e19cc`

— LeTuR's agent
<!-- fleet:worker u94-5d79a2bc9dc9c56d -->

v2.75.3

Toggle v2.75.3's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(core): write interface config atomically (#1598)

## Intent

Prevent a failed `ui.json` save from truncating the interface decisions
already on disk.

## What Changed

- Reused the atomic config writer for both `settings.toml` and
`ui.json`, with native replacement on Windows.
- Added an interruption regression test that constrains a child process
mid-write and verifies the old file remains byte-for-byte intact with no
staged file left behind.
- Preserved config-manager symlinks when saving and normalized Windows
destinations so native replacement works beyond `MAX_PATH`.
- Added Unix symlink and Windows long-path regressions.
- Documented the atomicity boundary and the lack of a storage-durability
guarantee.
- This is not visually observable; verification exercises persisted
config files and the real registry path.

## Risk Assessment

No format or migration changes. Runtime Windows replacement was not
exercised locally; all Windows tests compile for `x86_64-pc-windows-gnu`
and run in CI. The helper deliberately does not sync the staged file or
parent directory, so it protects readers from partial process writes
rather than storage or operating-system failure.

## Testing

- Review: two adversarial rounds; 2 findings raised and fixed (Windows
long paths and symlink preservation). `thurview` 0.35.0 was current, but
its documented `graph` subcommand is unavailable, so optional graph
augmentation did not run.
- Regression: the new test passes on this branch and fails against the
old direct-write call by exposing a truncated `ui.json`.
- `cargo check --tests --target x86_64-pc-windows-gnu --all-features`
passed.
- `just lint` passed.
- `RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features`
passed.
- `cargo nextest run --all` passed: 4,459 passed, 4 skipped.
- Documentation review passed; `docs/CONFIG.md` and the kernel/testing
references already describe the new contract.

— LeTuR's agent

v2.75.2

Toggle v2.75.2's commit message

Partially verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
fix(deps): update rust crate vt100 to 0.17 (#1595)

This PR contains the following updates:

| Package | Type | Update | Change | OpenSSF |
|---|---|---|---|---|
| [vt100](https://redirect.github.com/doy/vt100-rust) | dependencies |
minor | `0.16` → `0.18` | [![OpenSSF
Scorecard](https://api.securityscorecards.dev/projects/github.com/doy/vt100-rust/badge)](https://securityscorecards.dev/viewer/?uri=github.com/doy/vt100-rust)
|

---

### Release Notes

<details>
<summary>doy/vt100-rust (vt100)</summary>

###
[`v0.18.1`](https://redirect.github.com/doy/vt100-rust/blob/HEAD/CHANGELOG.md#0181---2026-10-19)

[Compare
Source](https://redirect.github.com/doy/vt100-rust/compare/v0.18.0...v0.18.1)

##### Fixed

- actually release the intended changes that were supposed to be in
0.18.0

###
[`v0.18.0`](https://redirect.github.com/doy/vt100-rust/blob/HEAD/CHANGELOG.md#0180---2026-10-18)

[Compare
Source](https://redirect.github.com/doy/vt100-rust/compare/v0.17.0...v0.18.0)

##### Added

- `Screen::contents_formatted_basic`, which is like
`Screen::contents_formatted`, but restricts escape sequences to SGR
escapes
  only, and control characters to `'\n'`.
- `Screen::write_contents_formatted_basic`, like
  `Screen::contents_formatted_basic` but writes to any type implementing
  `std::fmt::Write` instead of returning a `String`.
- `Callbacks::on_scroll`, called when a row scrolls off the top of the
scrollback. This enables capturing terminal output in a streaming
fashion.
Currently this can only be used to obtain the row contents in the "basic
formatted" style (the same returned by
`Screen::contents_formatted_basic`).
- `Parser::DEFAULT_ROWS` and `Parser::DEFAULT_COLS`, set to 24 and 80
(the
  same already used by `Parser::default`). These have type `NonZeroU16`.
- `Parser::set_size`, which is like `Screen::set_size` but calls the
`on_scroll` callback when a row is pushed off the top of scrollback
(which
  is now possible due to the other `set_size` changes).
- `Parser::screen_and_callbacks_mut`, which returns mutable references
to
both the screen and callbacks object at once. This was not possible with
  the existing methods.
- `capture::basic_formatted_to_plain`, which converts a basic formatted
capture (as returned by `Screen::contents_formatted_basic`) into a plain
  text one without allocating.
- `capture::basic_formatted_rows`, which splits a basic formatted
capture
  into individual rows without allocating.
- `capture::basic_formatted_to_plain_indexed`, which is like
`capture::basic_formatted_to_plain`, but also includes the original
index
  of each plain text substring.

##### Changed

- Bumped MSRV to 1.85 and edition to 2024
- `Parser::new` and `Screen::set_size` now take `NonZeroU16`s instead of
`u16`s. Passing 0 for `rows` or `cols` was not supported and resulted in
  panics. Additionally, `Screen::size` now returns `NonZeroU16`s.
- The following methods of Screen now return/yield strings rather than
byte vectors:

  - attributes_formatted
  - contents_diff
  - contents_formatted
  - cursor_state_formatted
  - input_mode_diff
  - input_mode_formatted
  - rows_diff
  - rows_formatted
  - state_diff
  - state_formatted

  The byte vectors were already always UTF-8.
- `Screen::set_size` resets the scroll region and scrollback offset when
the
  size changes, matching the behavior of xterm.
- `Screen::set_size` has different behavior when the screen height
changes.
Previously, it would discard rows from or add rows to the bottom as
needed,
  but this does not match most terminal emulators.

Now, when the height is decreased, `set_size` discards rows from the
bottom
only until the cursor is at the bottom. Then, it pushes excess rows from
  the top of the screen into scrollback.

When the height is increased, `set_size` pulls rows from scrollback into
the top of the screen. If scrollback is exhausted and the new height
hasn't
  been reached yet, blank rows are added to the bottom.

##### Fixed

- Fixed no_std support which was inadvertently broken in 0.17.0
- Changed handling of IL and DL sequences when the cursor is outside the
scroll region. These operations are now no-ops in that case, which is
the
  spec-compliant behavior.
- Fixed issue where one-row scroll regions could cause unrelated rows to
be
  marked as wrapped or not wrapped.
- Improved the performance of various operations, especially escape
sequences
  that insert/delete multiple rows or characters.
- `Screen::set_size` only clears each row's `wrapped` when the screen
width
  changes. Previously, it would unconditionally clear that flag.

###
[`v0.17.0`](https://redirect.github.com/doy/vt100-rust/blob/HEAD/CHANGELOG.md#0170---2026-10-18)

[Compare
Source](https://redirect.github.com/doy/vt100-rust/compare/v0.16.2...v0.17.0)

##### Added

- Support for DECSCUSR (set cursor style)
([#&#8203;21](https://redirect.github.com/doy/vt100-rust/issues/21),
reuben olinsky)
- Support for HPA (horizontal position absolute) and REP (repeat last
printed
character)
([#&#8203;23](https://redirect.github.com/doy/vt100-rust/issues/23),
KacperLa)
- no_std and embedded-io support
([#&#8203;25](https://redirect.github.com/doy/vt100-rust/issues/25),
Chris Pacejo)
- `Screen::scrollback_len`
([#&#8203;27](https://redirect.github.com/doy/vt100-rust/issues/27),
Erick Tryzelaar)
- Support for HVP (horizontal and vertical position)
([#&#8203;34](https://redirect.github.com/doy/vt100-rust/issues/34),
rezigned)
- Support for clearing scrollback with `Screen::clear_scrollback` and
`CSI 3 J`
([#&#8203;31](https://redirect.github.com/doy/vt100-rust/issues/31),
Alexey Zhokhov)
- Support for VS16 emoji-presentation sequences
([#&#8203;32](https://redirect.github.com/doy/vt100-rust/issues/32),
Junyi Hou)

##### Fixed

- Various bugs with oddly-sized terminals
([#&#8203;29](https://redirect.github.com/doy/vt100-rust/issues/29),
Evan Almloff)
([#&#8203;30](https://redirect.github.com/doy/vt100-rust/issues/30),
Junyi
Hou)
([#&#8203;41](https://redirect.github.com/doy/vt100-rust/issues/41),
taylor.fish)
- Scrollback now accumulates when a scroll region is active as long as
the
top of the scroll region is the top of the terminal
([#&#8203;33](https://redirect.github.com/doy/vt100-rust/issues/33),
L-jasmine)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/Thurbeen/thurbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xODAuMCIsInVwZGF0ZWRJblZlciI6IjQ0LjE4MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: LeTuR <magicletur@protonmail.com>

v2.75.1

Toggle v2.75.1's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
fix(core): reconcile session lifecycle and automation ownership (#1593)

## Intent

Make session creation, deletion, undo and purge recoverable across
crashes and delayed host replies. Refs #1469, #1509 and #1516.

## What Changed

Lifecycle operations record durable intent before resource changes and
use one reconciler to finish or roll back interrupted work. Creation
confirms pane identity before publishing; cleanup respects active pane,
checkout and workspace claims. Rollback preserves existing workspaces,
and a resumed launch cannot rebuild a workspace used by another active
session. Shared-host cleanup carries a lifecycle precondition, and purge
retains a minimal tombstone for peers.

```mermaid
flowchart LR
  I[Durable intent] --> R[Guarded resource changes]
  R --> C[Commit result]
  R -->|failure or crash| S[Shared reconciliation]
  S --> R
```

Automations can explicitly name an owner. Soft deletion suspends them
without replacing their IDs or history; undo restores their prior
enabled state after every owner/target suspension clears. Explicit
disable choices made while suspended survive undo. Force deletion and
purge remove owned automations and runs.

## Risk Assessment

Schema 59 keeps the minimum-reader version at 53. Older hosts without
the negotiated cleanup capabilities retain pending cleanup until
upgraded. Irreversible purge markers reject legacy revival and deletion
attempts until audit retention prunes them.

## Testing

Linux with real tmux: crash recovery, stale writers and host replies,
pane reuse, ownerless windows, checkout aliases, purge/retention,
automation undo and the existing three-process creation budget. Modeled
backends cover unstamped identity and host failures. Native
Windows/psmux and real remote hosts were not exercised locally.

Review is blocked on restore launch context and cleanup of a checkout
borrowed through another session's cwd or attached directory. Nine
end-to-end cases reproduce those failures on the published
implementation, including nested directories and directory lease
contention. Corrections and the full #1469 / approved PR-D acceptance
audit are in progress; prior review scores do not establish readiness
for the changed head.

The next revision must pass the full repository gate, normal signed
hooks, local current-head review by the independent merge reviewer, and
current-head CI and feedback. Earlier runs exposed terminal timing/input
failures and the Windows fixture's platform-route mismatch; no
assertions or timeouts were weakened.

Manual check on Linux: run `just build`, then `scripts/dev/sandbox.sh
--fresh --shell`. Use a disposable repository with a `main` branch:

```sh
thurbox-cli session create --name lifecycle-review --repo-path /path/to/disposable/repo --worktree-branch review/lifecycle --command sleep --arg 600
# Set this to the UUID printed above.
REVIEW_SESSION=replace-with-created-uuid
thurbox-cli automation create --name lifecycle-review --trigger hourly --command true --owner-session "$REVIEW_SESSION"
thurbox-cli session delete "$REVIEW_SESSION" --soft
thurbox-cli automation show lifecycle-review
thurbox-cli session restore "$REVIEW_SESSION"
thurbox-cli automation show lifecycle-review
thurbox-cli session delete "$REVIEW_SESSION" --force
thurbox-cli session purge "$REVIEW_SESSION"
thurbox-cli session list --deleted --include-purged
```

The automation keeps its ID and resumes its prior enabled state after
restore. Force deletion removes it; purge removes the created checkout
and leaves a tombstone hidden from the ordinary deleted listing.

— LeTuR's agent

v2.75.0

Toggle v2.75.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat(ui): clarify the sessions panel hierarchy (#1586)

## Intent

Make the Sessions panel easier to scan across hosts, repositories and
parent sessions. Refs #1551.

## What Changed

Hosts use accent bands, repositories use dotted separators, and session
trees use indentation and fold handles. Blocked rows stay visible under
folded parents and use a status highlight. Terminal titles stay beside
session names in narrow panes; wide panes align them; the optional title
setting displays the agent's OSC terminal title.

Folding a parent now selects it when the selected descendant disappears,
while preserving any selection that stays visible. Explicit focus
reveals every ancestor of a deep session; repeated ancestor IDs stop
malformed cycles. When a selected blocked child resumes work, its folded
ancestors open so the terminal stays on that child.

## Risk Assessment

The title setting uses the existing published OSC title, rather than
querying literal tmux `window_name`; that part of #1551 remains outside
this PR. Edited interfaces retain their existing layout. Native
verification is on Linux; Windows, live SSH and WSL rendering were not
exercised locally.

## Testing

Failure-first PTY and kernel tests reproduced folding onto a blocked
sibling, losing explicit focus beyond 64 parents, and switching away
when a folded blocked child resumes work. All 70 focused checks pass:
the complete session-list suite and three real PTY regressions,
including next-action targets and cycle safety.
A new real TUI regression first reproduced the terminal title missing
from a narrow session row, then passed after restoring it beside the
assigned name. Narrow/wide rendering coverage also passes. On the
current Linux tree, `just lint`, warnings-denied rustdoc and `cargo
nextest run --all` passed: **4,391 tests passed; four existing opt-in
tests skipped**. The isolated CLI `plugin check` passed with no
warnings.

`npm ci` and `npm run lint:website` passed. The built CLI's `plugin
check` returned `ok: true` and no warnings. Independent review and CI
status are reported separately for the exact head.

To try the interaction manually (the automated PTY tests exercise these
behaviors):

1. Run `just build`, then `scripts/dev/sandbox.sh --profile
hierarchy-review --isolate-home --shell`.
2. In that isolated shell, configure and seed the example:

   ```bash
   cat > "$THURBOX_CONFIG_DIR/agents.toml" <<'TOML'
   default = "shell"
   [[agents]]
   name = "shell"
   command = "sh"
   args = []
   TOML
lead_id=$(thurbox-cli session create --name lead --repo-path
"$TBX_REPO_ROOT" --agent shell --json | jq -r .id)
thurbox-cli session create --name quiet --repo-path "$TBX_REPO_ROOT"
--agent shell --parent "$lead_id"
waiting_id=$(thurbox-cli session create --name waiting --repo-path
"$TBX_REPO_ROOT" --agent shell --parent "$lead_id" --json | jq -r .id)
   thurbox-cli session signal --session "$waiting_id" --state blocked
   thurbox-cli config accept-interface
   THURBOX_UI_DIR="$TBX_REPO_ROOT/ui" thurbox
   ```

3. Click `quiet`, then the arrow beside `lead`. `quiet` disappears,
`waiting` remains visible, and `lead` is selected. Press `r`: the
confirmation must name `lead`; dismiss it with Escape. Expand `lead`,
select `waiting`, then fold `lead` again: `waiting` must remain
selected.
4. From another terminal, run `scripts/dev/sandbox.sh --profile
hierarchy-review --isolate-home -- session signal --session waiting
--state working`. The parent opens and `waiting` stays selected;
pressing `r` must name `waiting`. Dismiss with Escape.
5. Use F6 → Sessions to try all host/repository grouping combinations.
Quit with Ctrl+Q, exit the isolated shell, then run
`scripts/dev/sandbox.sh --clean hierarchy-review`.

— LeTuR's agent

v2.74.0

Toggle v2.74.0's commit message

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
feat(core): persist per-agent environment defaults (#1584)

## Intent

Select an agent account once and keep it stable across restart and fork.
References #1526.

## What Changed

Add an `env` table to agent definitions, resolve `{home}` on the launch
host, and save agent defaults together with session overrides.
Shared-host forks run on their owner and preserve its selected
conversation, hooks, and account environment. Configuration validation,
launch plans, session JSON, and the account setup recipe cover the new
contract.

## Risk Assessment

Per-account Codex hook provisioning (`{env:CODEX_HOME:-~/.codex}` in
hook manifest paths) remains unimplemented; this PR leaves #1526 open
for that accepted scope.

Existing sessions keep their saved environment; edited defaults apply to
new sessions. Saved values are visible in session JSON. A mirror reports
`env: null`; session-context launch plans and exec commands must run on
the owning instance. Native Windows and WSL were not exercised locally.

## Testing

Validation of the rebased review fixes is in progress. Failure-first E2E
tests exercise caller-owned direct-remote forks after sharing is
enabled, and injected environment/recipe save failures after worktree
preparation. Full local gates and current-head CI have not yet completed
for this revision. Native Windows, WSL, and real SSH integration are not
verified locally.

— LeTuR's agent